We're sorry but this page doesn't work properly without JavaScript enabled. Please enable it to continue.
Feedback

Introduction to Programming for Business Analytics - Lecture 4: Functions

00:00

Formal Metadata

Title
Introduction to Programming for Business Analytics - Lecture 4: Functions
Title of Series
Number of Parts
22
Author
License
CC Attribution 4.0 International:
You are free to use, adapt and copy, distribute and transmit the work or content in adapted or unchanged form for any legal purpose as long as the work is attributed to the author in the manner specified by the author or licensor.
Identifiers
Publisher
Release Date
Language
Producer
Computer animationLecture/ConferenceMeeting/Interview
Computer animation
Computer animation
Computer animation
Computer animation
Computer animation
Computer animation
Lecture/ConferenceMeeting/InterviewComputer animation
Transcript: English(auto-generated)
Welcome everyone to the fourth lecture of the Introduction to Programming for Business Analytics class. In today's lecture, we're going to talk about functions. The outline we have for today is at first,
we're going to talk about what is a function, and then we're going to talk about defining function in something called the standard form, and we're going to talk about function call and how they relate to the flow of execution, and then we're going to talk about different types of functions, namely parameterized functions and null array functions,
and we're going to talk about fruitful and void functions, and finally, we're going to conclude my important characteristics of functions. In the context of programming, a function is a sequence of statements that performs computations.
Of course, we have already seen several examples of such functions. Some of them we said were built-in functions, and some of them were user-defined. The built-in functions that we've used over the past few lectures were the print lin functions, the type of functions, link function, including others.
We also, in the first lecture, defined our own functions, such as the x squared function or the depth function that was related to the money example. In this lecture, we're going to focus mostly on the second type of function, user-defined functions,
and a user-defined function declared in a single expression, such as x squared is called an assignment or inline form. This is the distinction that we're going to make between this assignment or inline form and the standard form that we have planned for today.
Of course, the assignment form is convenient in that it reduces typing and visual noise, however, is actually restricted to computation declared in a single expression. Although these expressions can be compound expressions, of course, we did not talk about compound expression, and the link here is actually a hyperlink,
so if you click on it, it will redirect you to the Julia documentation, which describes what a compound expression is. But I think that compound expressions are beyond the scope of this course, so we're not going to talk about them, but if you're interested, feel free to take a look at the documentation. Nevertheless, the takeaway message is that the inline form or the assignment form
that we use to define things like x square and depth restrict us to a specific type of expressions that we can write in functions. In this lecture, we're going to focus on user-defined functions declared in the so-called standard or sometimes also referred to as the traditional form.
All right, so user-defined functions declared in the standard form have the following generic form. We have a header, we have a body, and we have a termination expression. So you might have already recognized this kind of a structure from the previous lecture. So the header starts with a keyword function,
which indicates that we want to define a function, and it is followed by the function name, and then the function name is the name that we use to call the function. And then we have the parentheses, which are called the limiters, and then between the parentheses, we have the different parameters of the function. Inside the body, we have the expressions or the part of the program we want to execute.
And of course, for readability, every expression in the body is indented, and then there is no limitation on the number of expression in the body. In fact, the fact that we have no limitation on the number of expression in the body is one of the reasons why we use functions in the first place, and one of the things that make functions very powerful.
And finally, we have a termination expression, and termination expression indicates where the function definition end. So the termination expression is also done using the keyword n. So we're gonna write an example of a function that takes two string values, or two parameters, first string and second string,
and concatenates them and prints the results. So let's do that, and we're gonna do it in the standard form. So what we're gonna write is we're gonna write function to declare that we want to define a function. And we say, we're gonna call the function concatenate underscore and print,
to just give it a name that symbolizes what it does. Okay? Of course, we're gonna say that this function has two parameters. The first one is called first string, and then the second one is called second string. And then we just close the parentheses.
Now we go inside the body, as you can see, Julia recognizes that we're trying to define a function, so it immediately indents the body. We're gonna define a variable, and we're gonna call it concatenated string, and then we're gonna set this guy equal to first string multiplied by second string.
Okay? And next line, what we're gonna do is, we're gonna print this concatenated underscore string. Then finally, we're just gonna type
int to end the definition of the function. Okay? Now we execute the cell. You'll see that it says concatenate print. We're not done here because we still need to call the function. To do that, we're gonna use an example where we use the function concatenate print
that we've just defined to concatenate, let's say, for example, the two word. The first one is water, and then the second one, let's say, is melon. Okay? So we execute, and we can see here, we have a new word that says water melon. So it basically created a new string or printed the new string that combines the two word together.
So let's see how function call fits within the flow of execution. So a function definition specifies the name of a new function and the sequence of statements that we want to execute once the function is called.
However, defining a function is not sufficient to get the desired output. We still need to call the function in order to get the desired result. It is actually helpful to think of function called as detours in the flow of execution. So what do I mean by that? Instead of going to the next statement,
the flow jumps to the body of the function, then the flow executes the statements inside of the function, and then finally the flow comes back where it left off. So here's sort of a visual representation of this entire process. So the flow of execution starts somewhere, we have several expressions, we evaluate them,
we go through them, and then at this point we get the function call. So what happens there is that we actually go, we jump out of this flow of execution, we go to the body of the function, whatever we define, in this case, we concatenate the two strings together, the first one and the second one, we print them, after that,
we come back to the flow of execution and we continue where we left off. So to help you create a mental model of functions in general in Julia, as they are very important, we're going to distinguish between functions along two dimensions. The first one, based on the input that the function takes, and then the second one is based on the output
that the function gives. We're gonna talk about something called parameterized and nullary functions, and then we're gonna talk about fruitful and void functions. So let's start with the parameterized and nullary functions. All of the functions we have seen so far are parameterized in that they have
at least one parameter. So we can have a unary function which has one parameter like x square, for example. We could also have a binary function, like the one that we've just seen so far where we have two parameters. So we have first string and second string. They also have a bunch of different aim. The terminology is really not that important here,
but the key message or the takeaway message is that we can have one parameter, we can have two parameters, we could also have three parameters like the depth example, or redefining the depth example that we discussed in the first lecture in this form here. We can have as many as n parameters. So in this example, we have a function that finds the product
of x or n values, in this case, n equals 10. So we have 10 here, the result is to multiply all of them together. All right, so by contrast, we can also have a function that does not take any parameter. And a function that doesn't have any parameters
is called a nullary function. So here's an example. We're going to have a function, which we're going to call print underscore greeting. And then between the parentheses, we're not going to have anything.
And then we're going to go inside the body of the function and we're just going to type in a string that contains hello world. It's just a very simple function to just illustrate the idea of not having to have parameters when defining a function or a function that doesn't have any parameter. Now, we go to the next line
and we call the function by typing its name. However, we type the open parent or we type the parentheses, but we don't type anything in between. And we execute the function and as you can see here, we have hello world. So note that although a nullary function that does not have any parameter, we still need to write the parentheses
when calling the function. However, as the function is nullary, we don't write any arguments between the parentheses. A special type of a nullary function is the built in function called read line. So the read line function provides a way of interacting with the user. So while the read line does not have any parameters,
it prompts the user to provide input. So in the example below, we're going to use a read line function to prompt the user to provide their name. And then we're going to assign the value entered by the user to a variable call name and then use it in another print statement. So you might sound abstract,
but as we write it down, it will become clear. So first let's say we want to just ask the user what is their name? So we can just type in a simple print statement that says, what is your name? And then in the next line,
we're going to utilize this read line function. And we're going to do that by assigning whatever the user types as input into a variable called name. The way we do this, we're just going to say read line. And then we have open parentheses and you'll see how it works in a second. And then in the third line,
we're going to have print lin and then between a string, we're going to say my name is, and then we're going to have comma here and name. As I mentioned, the value of name, it's going to depend on the value entered by the user.
So let's see how it works. So if I execute the cell, you'll see that I have this star symbol, which indicates that Julia is waiting for some output or Julia is still trying to execute this cell. And here we have a cursor or a blinking cursor
that indicate that we can type something. So I'm just going to type in, say Julia, okay? And then as we did before, we're just going to hit enter here. So once we've done that, the star changed to a number, meaning that the cell is no longer being executed. And as you can see here, we have, what is your name?
My name is Julia, okay? We have this word that says S-T-D-I-N and what it means is standard input stream. Note that the read line function reads the user input as a string. So if I, for example, say what is the type of name
and execute the cell, you can see here that it's a string, okay? All right, so now we're going to distinguish functions, not based on their input, but rather based on their output. We're going to start talking about fruitful functions.
So fruitful functions are functions that return a result. In Julia, the value returned by a fruitful function is the value of the last expression in the body of the function definition. So let's say, for example, I have a function definition that says that square, for example, is given by X raised to the power of two
and I just simply end the function. Of course, this function has only one line, but even if I add as many lines as I want, prior to that, the value returned by this function is going to be the value of the last expression evaluated in the body of the function. So if I execute the cell here
and I call the function by saying square four, you'll see here that I have 16, but regardless of how many line I put prior to that, so print len X here, and then we can also, the same here,
no matter how many such statement that I include, it always going to return the last value I'm evaluated in the body or in the definition. So unlike fruitful functions,
void functions are functions that perform an action but do not return a value. Well, in fact, void functions do return a value, but it's a value of a specific type called nothing. So let's take a look at an example. And to do this, I'm going to define a function that prints a string value twice.
So I'm going to call that print underscore twice, and then it's going to take a string. I'm just going to call it my string value here. And then in the next line, I'm just going to say print len,
and then just print this argument of the function, which is my string value, and then close the parentheses, and then same thing one more time. So my string, okay. Then I close the function. So I define the function by executing the cell.
And let's say, for example, I evaluate this function by saying print twice, and then let's just say we pass the word watermelon from the first example, okay. So as you can see here,
it's simply printed the word twice. Now, the difference might not be as obvious between a fruitful function and a void function, but to see that, we're going to use a built-in function called show, which is similar to print, but can handle values of type nothing, okay. So I'm going to define a variable.
I'm going to call it my square value, and I'm going to assign it to the return value of the function that we defined above, which is called square, okay. And then in the next line, I'm going to say show
my squared value, okay. So I type this incorrectly here. Then we need to close the print, okay. So if I execute the cell, maybe you can also redefine the function such that it doesn't print anything before that, because it's still remembering the other definitions.
So if I re-execute the cell, you can see here we have 25, okay. And then in the next cell here, we have result. I'm just going to call it print twice. And then inside here, let's just say hello, okay.
And then in the following line, we'll say show results, then execute the cell. Now, of course it printed the string that we passed as a parameter or as an argument to the function twice.
Hello, hello here. But then in the last line, instead of saying a number, it says nothing. Like I said before, that's a specific value, and it's a value of type nothing. So if you ask Julia, for example, what is the type of result? It's going to say nothing, okay.
The takeaway message is that a fruitful function is whenever we have a function that returns an actual value, but a avoided function returns a value of type nothing. All right, so a very important keyword
when it comes to a fruitful function is the return statement. So the return keyword is typically followed by an expression, and that expression is the expression we want to retrieve the value of. And the return statement means that we want to return immediately from the function, and we want to use the following expression as a return value. To see this, consider the following function
that calculates the square root of A using Newton's method, which we discussed in the previous lecture. And we're going to give it as a input, a initial guess of X, okay. So let's write it down. So first we're going to write function to indicate that we want to define a function. And we're going to give it the name
Newton's underscore square root, okay. And then between parentheses, we're going to have A and then comma X. Of course, A is the value, and we're trying to find the square root of, and then X is our initial guess. And in the first line, what we have is Y equals to
X plus A divided by X divided by two. So that is just the first update using the formula, if you remember. And in the next line, we're going to write the Y loop that iterates until the value of X is equal to the value of Y. So we write while Y is not equal to X,
what we're going to do is we're going to set the value of X equal to Y. In the next line, we're going to set the value of Y. We're going to update the value of Y using the formula again. So X plus A divided by X, and the whole thing is divided by two.
Finally, we closed the loop here. And then we're going to add this keyword return followed by X. We complete the definition by typing in and executing the cell. So we have Newton's square root method. That's the definition, it's complete.
So we can call the function. So for example, we're going to define a variable here. We're going to say square root, and we're going to assign it to the output of Newton's underscore square underscore method, and which take two parameters. And let's say, for example, we want to find the square root of 81
using 0.0001 as our initial guess, okay? And then in the next line, we're just going to print the value square root, the value of the variable, which we assigned the output of the function to. Executing the cell, we have a typo somewhere.
So it says undefined error, that this is not defined. So Newton's square root. So instead of square root, I wrote square method. So re-executing the cells, as we can see, we have nine, which is the correct output.
So suppose now I want to redefine the function in such a way that we don't have the condition that terminates the loop in the header. In other words, what I'm going to have here, I'm going to replace the sky by the keyword or the Boolean expression true.
And then I'm going to add an if statement here after the update step. And in the if statement, what I'm going to check for is if the value of y is equal to x, I'm going to return the value of x, okay? And then I can get rid of the one here at the end.
So just to see how this return keyword actually work. Of course, we can, just to make sure that we have a different function, we can just going to give it an underscore two at the end to make it distinct from the one that we've already defined, okay? So it's very similar to what we have before, except now we, instead of writing
the termination condition in the header, we put it inside the body of the function. And in particular, we have a condition that check for whether y value is equal to x. And if that happens, that's what we're going to return from that function, okay? So we execute the cell to complete the function definition.
And so now I have to write down everything again. We're just going to copy and paste everything here. And we're just going to use the new function that we've just defined, which we added underscore two at the end of its name. So executing the cell, we'll see here that we can still get the same result, okay?
In other word, this return keyword acted as a way to break out of this while loop that we have. Note that we can actually use the return statement to return not only one value, but also multiple values.
So in the previous example, we were only returning the value of x, but in the following example, we're going to return the value of x, which corresponds to the square root of a and the number of iterations it took to find its value using the muting method. So without having to type anything, everything from the start, so we're just going to use the same blueprint
that we have for two, and we're just going to call it three this time. And what we're going to do is we're going to define, say for example, a new variable, we're going to call it, let's say iteration equals zero. So this is going to be the iteration count that we have. And in the if statement that we have,
so we're going to say, if y equals equals x, what we want to return is we want to return x and the iteration that we have. Of course, we need to make sure that we are incrementing the iteration every time we go through the loop. So if y is not equal to x,
this means that the condition has not been satisfied, then we're going to do another round of iteration. And to do this, we're going to use a conditional and incorporate that as in the second branch of the alternative conditional execution statement that we have. So in the second branch, what we're going to have is iteration plus equals one.
So putting everything together, we have initialized this iteration counter and we have the first update step. So we have the while loop that says, while something is true, do the following. So iterate, if the condition is satisfied,
we're going to return the value of x as well as the number of iteration it took to do the update. Otherwise, we're going to increase the iteration count by one. So executing the cell and using the structure that we have or the function call that we have for Newton's method two,
we're going to now change that to three, of course, because that's the new function that we have. And note that return now gives x and the number of iteration. So in order to retrieve the number of iteration, we're going to add a comma here and say number of iteration as well.
So that's two values that we want to obtain. And we're going to print the square root as well as print maybe in a new line, the number underscore of iteration, okay?
Maybe we can also add println to the square root value so that we can see them clearly. So when we execute the cell, you'll see here that we have the println square root is nine and then the second line is 23. So that's how many iteration it took to find the square root of 81
using .00001 as an initial guess. So in addition to having multiple return value, we can also have multiple return statements inside of a single function. So in the following example, we're going to use two return statements
to return the absolute value. And as you know, the absolute value of a number depends on whether its value is positive or negative. And here we're just going to use x as variable. So let's see how the function looks like. And hopefully it will become clear how we use multiple return statements. So we're just going to use that. We're going to call that an absolute value.
And between the parentheses, that function takes just one parameter, which is x. So what we're going to evaluate is just going to say, if the value of x is negative, meaning that it's less than zero, what we're going to return is return minus x,
because we want to find the absolute value. So we multiply by minus one and it becomes positive. And in the second branch, it's going to be greater than or equal to zero. So we're just going to return the value of x as it is. Okay, we close statements here
or close the alternative execution statement by typing the keyword. And finally, we close the function by typing the keyword end as well. Complete the definition by executing the cell. So here you can see that if we say, for example,
what is the absolute value of say negative one and execute the cell, you'll see that it says it's one. Okay, we can also do that for other numbers like zero, for example, of course we don't need
a negative zero, so we can just type zero. And then finally we have one here. And for all of these numbers, it indeed gives the absolute value of the corresponding number. So all of the fruitful functions we've seen so far has a return value that is numeric, but a fruitful function does not have to return a numeric value.
A common return type of a fruitful function is a Boolean value. The following example shows a fruitful function that returns true or false depending on whether X is divisible by Y or not. This is something that we've used extensively in the previous lecture, and we're gonna formalize that or we're gonna put that in a function.
So we're gonna call that function is underscore divisible. And it has two parameters, X and Y, and that's what it checks for, whether X is divisible by Y or not. So inside the function, we check for if X is divisible by Y, and we do that using the modulus operator.
So if X modulus operator Y equals equals zero, what we want to do is we want to return true, right? That's what the function tells us, that X is indeed divisible by Y. Using this alternative conditional execution statement, in the else branch, we're gonna have return false, right?
That's the second alternative. And then we close the alternative execution statement here. And then finally, we close the function definition here. Once we execute the cell, we can give it or we can test the function by let's say, for example,
given it two parameters or two argument, let's say five and two. So as you can see here, it says false because five is not divisible by two. But if we say, for example, four and two, it says that four is indeed divisible by two.
All right, so now we wanna discuss some of the important characteristics of functions. The first one is related to this idea that is called scope of variables. Actually functions introduce this thing called a local scope. And a scope of a variable is the region of code within which a variable is visible.
So we're gonna give it a couple of example and hopefully it will become clear in a second. So variable scoping helps us avoid variable naming conflict. And the idea is actually quite intuitive. So if you have two functions, both of them can have arguments with the same name without two names referring to the same thing.
Similarly, there are many other cases where different blocks of code can use the same name without the name referring to the same thing. So let's take a look at an example. So we're gonna define two functions. The first one is called add.
And it takes two parameters, x and y. And what it simply does is that it adds the value of y to x, okay? And what we're gonna do here, we're just gonna say plus equals y, okay? Because we wanna add that to the actual value of x that we pass to the function.
And we're simply going to close the function here, okay? And then in the next line, we're gonna have function called subtract. It also takes x and y as parameters. But now instead of adding the value of y to x,
it subtracts the value of y from x, okay? So we have x minus y here and we close the function by typing the end keyword. So we execute the cell, we execute the cell to complete the function definition. So let's see how this scoping idea works.
So if I say, for example, that x equals one and y equals three, and then I call each function and I do that by assigning the value of the add function to a variable called add results, right? And I still use x and y here.
And in the second line, of course, I have the same thing. But now instead of having add, I have subtract. And then I'm just gonna call that sub results, okay? So I execute the cell. Of course, here it's giving me the value of,
the last thing that I input here, but maybe we can suppress that so it's not too distracting. And in the next cell, what we're gonna print is we're gonna print the value of add results, what we obtained by calling the add results function.
We're also gonna print the new line, the value of sub result. And then we're also gonna print the original value of x and the original value of y, okay? Executing the cell, you can see that the value of add results is four,
the value of sub result is negative two, the original value of x is one, the original value of y is three. So although we use x and y in this function, as well as this function, and the first one actually adds the value of y to x and the second one subtracts the value of y from x,
none of them had an impact on the result of each other because one plus three we know that is four, and then one minus three we know that it's minus two. So we get the correct results for both of them and we still have x and y to have the same values as well.
The really important thing when it comes to scope of variables or local scope is that variables that we define inside the function actually disappear whenever the function ends. So I'm gonna write an example of the function add and we're gonna see how a variable disappears once the function ends.
So we said that we have a function called add and it takes two parameters, x and y. But now instead of adding the value of y to x by adding it directly to x, I'm gonna define a variable, I'm gonna call it z, and that variable equals to x plus y, okay? And then inside the function here I'm just gonna print in the value of z, okay?
So maybe you can put that also here in a string and this says z equals dollar sign z, okay? And then we're just gonna end the function here and we execute the cell. In the above example the variable z
is going to be local or is local so when add terminates it gets destroyed and if we try to print it we get an exception. So let's actually do that and try to print it inside or outside the function. Of course we have this print statement that prints it from inside the function but we're also gonna do that from the outside as well.
So we have x to be equal to one, y to be equal to say three, and then what we want to do is we wanna have add here x and y. That is in the first cell, of course we expect that it prints the value of z as well.
After we do that we're also going to say can you also print lin value of z? Maybe you can also distinguish between dividing the parameters as well as the printing of the z variable from inside and outside. So here we find the variables and also suppress that
so it's not too distracting. And we call the function and as you can see here we have z to be equal to four. That is just a statement from the inside. And whenever we call the print lin function using the variable z or passing the variable z from outside of the function you'll see here
that Julia says that undefined error z is not defined. All right, so the second important characteristic about function that we want to discuss is something called argument passing behavior. So in Julia whenever we define a function argument values are not copied
when they are passed to the function. Instead they act as new location that refer to the values. However, the values they refer to are identical to the past values. As a result whenever you pass a mutable value such as a vector the function gets a reference
to the vector. So if the function modifies the vector then the original value or what is known as the color will also sees that change. So let's see an example and hopefully this will become clear. So we're gonna define a function. We're gonna call that function
at underscore n two underscore list, okay? This function takes two parameters n and list. And what it simply does is that it's going to define a new variable
and we're gonna call it say for example my temporary list or my temp list. And this temporary list is gonna be equal to list here. And we're gonna simply push to this new list that we've defined this temporary list
the value of n that we are passing as parameter as well, okay? So in simple terms this function pushes the value of n to the end of list. But the way we do this inside the function we're gonna create this sort of local copy
of the list variable or of the list array or vector. And we're gonna assign that to a new variable called my temp list. And then we're gonna push n to this my temp list. So let's execute the cell to just complete the definition.
So let's define a list here or a variable called list and just gonna be an empty list, okay? And then in the next cell what we're gonna do is we're gonna print this list before and after passing it to the add n to list function that we've just defined.
So here we're gonna have say print lin list and then in the next line we're going to add n to list using the function that we've just defined and say for example we just wanna add the number one to the list that we have and then in the following line
we're gonna have print lin list. So what we're doing here is that we're printing the value of list before and after calling the function add n to list. And let's see what happens. So when you call the function as you can see here before executing the function,
the array or the vector list is empty but after that is one. In the above example although we pushed the value and to the temporary list my temp list defined inside the function call the change was also reflected in list. So that is why we mean by the caller also sees the change
and that whenever we pass a argument value that is mutable such as vector the function gets a reference to the vector. So in this sense here the value or what we pass inside the function tells Julia that list here is also referring to this list.
All right, so we're now onto the last topic or the last section of this lecture where we talk about recursive functions. So a recursive function is a function that calls itself during the execution. Now it might not be obvious why a function
might or could call itself for that matter but it actually turns out to be one of the most powerful things a computer program can do. So to put this into context let's actually consider a number n and let's suppose that we want to calculate all the positive integers less than or equal to n
or rather the product of all the positive integers less than or equal to that number n. And we usually call this factorial and it's denoted by n followed by an explanation mark and it's given by the following equation. So n factorial equals to this number n
multiplied by all the positive integer less than or equal to that number. So we have n multiplied by n minus one multiplied by n minus two and so on until we get to one. One thing to note is that typically we assume that zero factorial is equal to one.
So this recursion or this subtraction from n continues until you reach zero factorial which in turn gives you one. Now note that the factorial of n can also be written as the product of n with the smallest next factorial. What does that mean? So here we have n factorial
and if we look at this two or this entire line as two separate parts the first part consists of this n and then multiplied by everything that follows. Now everything that follows is actually just n minus one factorial because here we're actually multiplying n minus one
multiplied by n minus two and so on. So that same pattern repeats all the way until the end. In other word we can say that n factorial is equal to n multiplied by n minus one factorial because once you break this down this n minus one factorial you're gonna have n minus one multiplied by n minus two
multiplied by n minus three and so on until you get to the end. However, we know that as I just mentioned that zero factorial is equal to one. So here we have this two sort of condition that says if the value of n we're trying to calculate the factorial for is equal to zero
then the return value is going to be one. And if that value of n for which we're trying to calculate the factorial is strictly greater than zero then the return value is going to be n multiplied by n minus one factorial. So this definition says that the factorial of zero is one and the factorial of any other value n
is n multiplied by the factorial of n minus one. So we just subtract one. So suppose we want to calculate three factorial what we're gonna have here is that three multiplied by two factorial because as we know that n is strictly greater than zero or three is strictly greater than zero therefore it falls into the second category.
And then we know that n factorial we know that two is also strictly greater than zero therefore we have two factorial to be equal to two multiplied by one factorial. One is strictly greater than zero so we have that one factorial equals to one multiplied by zero factorial.
And we know that zero is not strictly greater than zero and we know that zero factorial is equal to one by the first piece of this function. So this is actually called a piecewise function because it has two pieces. The first piece follows this condition and the second one follows this second condition. So if we put everything together
what we have here is that three factorial equals three multiplied by two factorial. So this is everything here and we know that two factorial is one two multiplied by one factorial so two multiplied by one factorial. Inside the one factorial here we have one multiplied by zero factorial and we know that zero factorial is equal to one
by the last line here. If you multiply everything here we have one, one multiplied by two, that's two two multiplied by three, that's six, okay? Now, if you can write a recursive definition of something, meaning that in this piecewise function
we said that n factorial is a function of something here that depends also on n minus on some other factorial. There is this recursion procedure in the sense that n factorial also depends on some other factorial inside the function. And if you can write a recursive definition of something
you can write a Julia program to evaluate this. So before we write this, before we program this just a small note that I would like to make is that Julia has a built-in function called factorial which calculates the factorial of non-negative integers and to avoid naming conflicts we're gonna use the name my underscore factorial
to just make the distinction between the built-in function and the one we're about to define. Okay, so how do we define this? So the first thing we do is clearly we just write function, that's the keyword to define the function name and then here my underscore factorial that's the name we just agreed upon.
And then here it's actually a function of n, okay? That's the argument of the function. Remember what we're trying to achieve is we're trying to recreate this piecewise function, okay? So that's the header and then we always make sure that we want to close this function and what happened here is that what happens
in the body of the function is gonna follow exactly what this piecewise function says. So this piecewise function says if n is equal to zero then what we're going to return is we're going to return one. So we write that exactly. So we say that if n equals equals to zero here we want to return the value of one, okay?
Else that's the other part of the statement where when I return is we want to return n multiplied by n minus one factorial, okay? Because we know that otherwise n is going to be strictly greater than zero and we obviously assume that n here is a strictly positive number, okay?
So here we want to return n multiplied by n minus one factorial. Now we are still defining this factorial function but we said that a recursive function
is a function that calls itself. So what we're going to have here, we're going to say that we also want to do a factorial here but this factorial inside this function is going to be with n minus one instead of n, okay? So this actually complete this definition of this recursive function and that defines a factorial.
We execute the cell and then finally here we have my factorial we just want to test the function. We'll use the same number that we used in the example which is six so when we execute this function or excuse me we use three. So when we execute this cell here we can see that
the value of my factorial or the value of factorial three is six, okay? So as you've just seen if we call the function my factorial with the value three the flow of execution will look as the value going to be returned
is going to be six and to sort of see what's happening or to try to understand what's happening behind the scenes of how this function or this recursive function operates we're going to take a look at the following. So this is how the flow of execution looks like. At first here this function says if n equal to zero
return this else we're going to return n multiplied by my factorial n minus one. So when n is equal to three that's when we call it the first time since n is not equal to zero so the first branch of the condition is not satisfied we're going to return the factorial of n multiplied by my factorial n minus one, okay?
But the thing is my factorial n minus one is also has not been calculated. So the next time we're going to be here with n equals to two, right? Because we pass in three minus one here that's going to be two. And again once we enter this function again we're going to check that we're going to find
that n is not equal to zero and therefore we're going to take the second branch once again so we're going to evaluate this time n multiplied by my factorial two minus one which is going to be one. Remember here we're going to be multiplying also by two because that's the argument that we pass to the function in the second call, okay?
In the third call again n is not going to be equal to zero it's going to be one we take the second branch and calculate the factorial of n minus one so it's going to be one minus one which is zero. So once we get to zero the first branch is going to be evaluated and then we're going to be returning one directly without making any recursive calls.
So that's the first one we're going to return without any recursion. So now we have one, so one gets evaluated but now we're going backward, right? So one is going to be multiplied by one here that's the first part of the recursion. So return one, the return value is multiplied by n one because here that's corresponding to this first guy
and the result is going to be one multiplied by one is returned, okay? We get one here return and then the return value is going to be multiplied by two, okay? That's the one corresponding to this guy here and we're going to have one multiplied by two that's two and finally this two is going to be returned to the first line here and we're going to have
the return value multiplied by n equals three and then we have two multiplied by three that's equal to six, okay? But there is one really important part in any recursive function which is the fact that this recursive procedure at some point
is going to stop whenever we reach zero, right? So we are calling, the function is calling itself, calling itself, calling itself sort of going downwards but at some point it reaches this point where it says, okay, now I have a fine value to work with which is going to be zero factorial and that's going to give us one and this value is going to now be sort of
propagated backwards so the one is going to be returned to the one multiplied by one, the new one is going to be returned to the two multiplied by one, the two is going to be returned to the three multiplied by two which finally gives us six, okay? So this point at which this recursive procedure stops
is called or this expression where the recursive procedure reaches a termination point is called an anchor, okay? Every recursive function must have an anchor that is the most important thing in any recursive function is to have a place at which calling the function itself stops and then we can use this new value
and propagate it backwards in order to get the final output so to just see how this all looks together I'm gonna sort of write down a function just to help us visualize how this recursive procedure works. Okay, so we're gonna start by defining the function
so we'll just use the keyword function and we're gonna give it a slightly different name I'll call it my visual factorial just to make the distinction between the one that we defined previously and obviously I say my visual because we're gonna add some print statement there that are going to help us visualize how this recursive procedure works.
The first thing I'm gonna define I'm gonna define a variable I'll call it space and this variable I'll assign it to something that as you might expect will define a space and I'll do this by having the string value with empty space in the middle and this will be raised to the power of
four multiplied by N. So what I'm trying to do here is to have a space that depends on the size of N factorial that's being called within each call of the recursive function. You'll see how this will come together in a second.
So we'll have here print then we'll print in a new line this space that we've just defined and then comma followed by factorial N. So every time the bigger N there's gonna be a bigger space and then followed by the string value
that says factorial followed by the value of and there's currently being called. And now we're gonna have the components of the actual recursive function from last time. So it's just if N equals equals to zero that's the part where we're going to return one but before we do that we just wanna print another statement
or add another print statement that says print the space that we have just specified followed by a string that says returning one, right? That's the part of the if statement where we're turning. And then we just also want to return one. And then here we have else that's the second part
of the second branch of this condition or this condition execution. We're gonna define another variable I'll call it recurse here. And in this recurse we're gonna assign it to the value of my visual underscore factorial just like we did last time.
And now we're gonna call it with N minus one instead. And we're gonna have the result to be equal to N multiply by the value of recurse. So as we did last time except now we're just breaking it down into two variables.
And the reason why we do this is because we want to add this print statement here that says, well, first we will create the space and then we have inside of the string statement that says returning this corresponding result.
And finally, we're going to return the result that we've just calculated. And we must make sure that we also close the if statement that we've just defined. And so now once we execute the cell, we have the function that we've just defined and we call the function as we usually do by saying my visual underscore factorial.
And then between the parentheses we have three. So that's the example that we've done previously. And as you can see here, the first time N is strictly greater than zero. The first branch is not evaluated. The second branch is evaluated.
We're calculating the factorial of three. We go and calculate the factorial of two, factorial of one until we reach the factorial of zero. We know the factorial of zero returns one. And then one gets multiplied by one here, which is this N factorial. And then this returns two. And then two gets multiplied by three here,
which gives us six at the end. All right, so just to summarize, user-defined functions in the standard form are usually declared using the keyword function. And the expression for defining functions in the standard form usually consists of a header, a body, and a termination expression or termination keyword.
And we said that we can think of function call as a detour in the flow of execution. We also talked about different types of functions. Specifically, we said that functions can be nullary, meaning that they don't have to have parameters, but they can also be parameterized by one or two or even more parameters.
And they can also be either void or fruitful. And we also talked about the return statement, which says that once we write a return statement, we mean that we want to return immediately from the function and use the following expression as a return value. We also talked about variable scoping,
and we said that the scope of a variable is the region of code within which a variable is visible. And finally, we talked about recursive functions, and we said that a recursive function is a function that calls itself during the execution, and we gave an example of that using n factorial.
Looking forward, we're gonna actually talk about the last section of this first chapter. In particular, we're gonna talk about some advanced data types, such as tuples or tuples, as well as multidimensional arrays,
dictionaries, and structs. That's it for this lecture, and I'll see you in the next video.