Last week, you have gotten to know functions as essential building blocks of programs. This week, we'll still be concerned with functions. We will get to know several ways to construct them and to compose them. Finally, next week, we are going to look at data and objects. In the first session of this week, we are going to look again at recursion, and we will find out that there are actually several ways to express recursion. First let's review function application. You know from the last session that there's one simple rule. You evaluate a function application F applied to arguments E1 to EN by evaluating the arguments, expressions E1 to EN they would resulting the values V1 to VM say. And then replacing the application of the function by its body. And at the same time replacing the forming parameters of the function by the actual arguments V1 to VM. This rewriting rule can be formalized by rewriting the program itself. Say, you have a program with a function definition def f with parameters x one to x n and a body b. And then, somewhere else eh, you have a function call f applied to value argument values v 1 to v n. That program can be rewritten to a programme that contains the same function definition but the function application has now being replaced by the body of f, B, and at the same time the former parameters x 1 to x n have been replaced by the argument values. And the rest of the programme is assumed to be unchanged, which is assumed to be these triple dots here. That's areas of the programs that we do not touch. This notation, we want for x 1 the end for x n is called a substitution. What we mean by it, is that all occurrences in b of the values x one to x n have been replaced by the corresponding values v1 to vn. Here's an example, consider GCD, the function that computes the greatest common divisor of two numbers. We know an implementation for GCD it is since a way long time, since ancient greek, its known as Euclid's algorithm. To compute the greatest common divisor of two numbers A and B, what do you do. First if B is zero then A is a common divisor of both A and B and otherwise you compute the greatest common divisor of. First b, and then a modular b. Percent sign here is as in Java and C, means that A modular B, the modulus of A and B. Let's try this algorithm in action. Gcd, fourteen, 21. How is that evaluated? Well, you would expand GCD, so that would give you, if 21 = zero, fourteen else GCD, 21 and fourteen modular 21. And you simplify the condition, 21 = zero is false. Next you simplify the if-then-else using the rule we have developed previously. So if false, fourteen else, GCD of 21 and fourteen modular, 21 would give the else part. Once we have the else part, we reduce its arguments to values. That gives us an other call to GCD which we expand, and the process repeats. I have signified that here with a multiple errors to say these are reductions that take more than one step. We get GCD fourteen to seven, we get GCD seven, zero, and finally you get the condition if zero equals zero, seven else some other call to GCD. And that finally, since the condition is true here, we get seven. Okay. Let's look at another rewriting example. Factorial. Classical algorythm for factorial is the following. You're said to take the factorial of a number n. You ask again if n equals zero, then the factorial would be one. Otherwise it would be n times factorial of n minus one. Let's apply that algorythm to factorial of four. So that would expand to the following conditional. If four equal zero, one, that's four times factorial four minus one. You evaluate the if then else, that gives you in a couple of steps, four times factorial of three. The second part. Reducing further gives you four times three times factorial of two. And so on, until you finally reach four times three times two times one times one. That was the last, the first case of factorial and that reduces to 120. Question, what is the difference between these two sequences? GCD and factorial? Well, one important difference is that if we come back to GCD, we see that. The, reduction sequence essentially oscillates. It goes from one call to GCD to the next one here, to the next one here, to the next one here and finally it terminates. In between we have expressions that are different from the simple call like if then else's but we always come back to this shape to the call of GCD. If we look at factorial, on the other hand we see that in each couple of steps we add one more element to our expressions. Our expressions becomes bigger and bigger until we end when we finally reduce it to the final value. So, that difference in the rewriting rules actually translates directly to a difference in the execution, in the actual, execution on a computer. In fact, it turns out that if you have a recursive function that calls itself as its last action, then you can reuse the stack frame of that function. This is called tail recursion. And by applying that trick, it means that a tail recursive function can execute in constant stuck space, so it's really just another formulation of an iterative process. Could say a tail recursive function is the functional form of a loop, and it executes just as efficiently as a loop. So if we go back to GCD, we see that in the else part, GCD calls itself as its last action. And that translate its, to a rewriting sequence that was essentially constant size, and that will, in the actual execution on a computer, translate into a tail recursive call that can execute in constant space. On the other hand, if you look at factorial again, then you'll see that after the call to factorial n minus one, there was still work to be done, namely, we had to multiply the result of that call with the number N. So that call here is not a tail recursive call, and it becomes evident in the reduction sequence, where you see that actually there's a buildup of intermediate results that we all have to keep until we can compute the final value. So that factorial would not be a tail recursive function. Both factorial and GCD only call itself but in general, of course, a function could call other functions. So the generalization of tail recursion is that, if the last action of a function consists of calling another function, maybe the same, maybe some other function. The stack frame could be reused for both functions. Such calls are called tail calls. After having gone through the exercise, you might ask yourself, should every function be tail recursive? Well, not really. The interest of tail recursion is mostly to avoid very deep recursive chains. Most implementations of, the JBM, for instance, limit the maximal depth of recursion to a couple of thousand stack frames. So if your, the input data is such that these deep recursive chains could happen, then yes it's a good idea to reformulate your function to be tail recursive, to run in constant stack frame, so as to avoid stack overflow exceptions. On the other hand, if your input data are not, susceptible to deep precausive chains then clarity trumps efficiency evert time, so write your function the clearest way you can. Which often is not terricosive. And don't worry about the steck frames that are spent. As Donald Knuth has said, premature optimization is the source of all evil. And that's the model that's very valuable to follow. Coming back to factorial. You might have observed that factorial grows really very, very quickly. So, even after very low number of recursive steps it will already the, exceed the range of integers, or even long integers. So, in that case definitely it was not worth making factorial a tail recursive function. So, we did that only as a, an exercise so that you have the techniques ready when you need them. So let's do an exercise on tail recursion. You've seen factorial, and we've seen that, that version of the function was not tail recursive. So now the task is to design a tail recursive version of the same function. Okay, so the most convenient way to solve this exercise is using another worksheet. I have already created a new package in our proc fun project called week two, and in that package we have a worksheet excercise in which I have already written the signature of factorial but I've left out the implementation. So, how would we implement that in a tail recursive fashion? Actually, it turns out that we need another function, called loop, that takes two parameters, not one. I'll give you an outline of loop here. So, the idea is, it would take an accumulator. That's also an int, and the current value and it would ask if, the current value is zero then it wouldn't return one as before. But the accumulator and otherwise, it would return something else. And we would start the program with loop of an initial value of the accumulator and the current value of n. So the question is, what do we need to do to fill in the question marks here? The first task is to fill in something for the initial value of the accumulator. Here it's pretty clear that, we say, well, if n equals zero then the accumulator is returned so it has to be the right value for n equals zero. And that would be one. So we pass one here. The second question is, what do we do in the case where n is not equal to zero. And the idea there is we would call again into the loop with the accumulator times and the current value and n reduced by one. And the thing still doesn't compile because we have forgotten to give a result type to loop. Once we do that, factorial compiles. And we can test it so let's do that. So that would give 24 as expected. In this session we've gotten to know tail recursion as a special case of recursion that corresponds to a loop in an imperative program. In the next session, we are going to cover new ways to compose functions.