In this session, we're going to look at another set of operators that serve for reducing lists. In fact, it's quite common to combine the elements of a list with a given operator. For instance, to sum all the elements of a list, he could use an operation like this one, so x1 plus xn. And for the empty list it would return zero. Or to take the products of all elements you would arrive at this formula, multiply all the elements of the list and multiply by 1, which gives you 1 for the empty list. We can implement this with the usual recursive schema. So for instance, for sum it would look like this. Sum xs takes a list of inputs towards an end. Xs match if it's Nil then it's 0. Otherwise, if it's a list starting with y, it's y plus sum of the remaining elements ys. This pattern can be abstracted out using the generic method to reduce left. ReduceLeft inserts a given binary operator between radiation elements of a list. So if you have a list of x1 to xn and you do a reduceLeft with op. And that would give you x1.op, x2.op, x3 and so on until op(xn). If you have reduceLeft then we can simplify sum of the product as you see here. Sum would be just 0 cons xs reduceLeft with the summation function. And product would be 1 and xs reduceLeft with the multiplication function. As an aside, we can write these functions that we pass to other functions also in a shorter way. Instead of xy arrow x times y for instance, one can also write shorter underscored times underscore. Every underscoring the formula represents a new parameter going from left to right and the parameters are then defined. So this thing here would be inserted at the next outer pair of parentheses or the whole expression if there are no enclosing parenthesis. So essentially you go left to right. You know the occurrences of an underscore. Once you hit a pair of parenthesis, you insert a parameter section, one parameter for each underscore that you have encountered. So with that, sum and product can also be expressed like this, which is aesthetically a bit more pleasing. So sum would be zero followed by excess reduceLeft with plus and product would be 1 followed by xs reduceLeft and multiply. The function reduceLeft is defined in terms of our more general function foldLeft. FoldLeft is like reduceLeft, but it takes an additional argument and accumulator named z here. That accumulator is returned when foldLeft is called on an empty list. So if we take a list of elements x1 to xn to a foldLeft with the zero z and the operation op, then that would be z.op x1.op x2 and so on until op(xn). Or visually it would look like this. So you have z on the left and you have the x1 here and you have the operation and you build up operations until you finally combine with xn. So it's a left leaning tree with operations at the node and z at the lower left corner. And the elements x1 to xn as the leaves on the right. So with foldLeft, we can now define sum and products as follows. Sum of xs would be excess foldLeft with 0 as the identity and plus as the operation and product wolf of xs would be xs foldLeft with 1 as the identity and times as the operation. So now that we have foldLeft, we can define reduceLeft in terms of it. So reduceLeft takes an operator from T and T. The list element type two T and returns a T and its body is defined only for non empty list. So if the list is empty, we throw an unsupported operation exception. If the list is non empty x followed by xs, then we do a foldLeft on xs with the zero element x or the first element and the given operation. So we've seen that applications of foldLeft and reduceLeft can be visualized as trees that lean to the left. There are also two dual functions foldRight and reduceRight, which produce traits which lean to the right. So here are the definitions of reduceRight and foldRight. Let's look at foldRight first. So if we do a foldRight on a list x1 to xn with a zero z in an operation and we get this right hand side. And if we want to draw that, then it looks like this. So it's x1 op, x2 op, and then the list goes down until finally we have xn op and Z. So we get a right leaning tree, the spine of the trees. Again, the operations, the elements appear from top to bottom x1 to xn. And the Z zero is on the low right corner and reduceRight is like foldRight. But instead of having a separate zero, it just combines the elements in the list itself. So it would look like this. So foldRight and reduceRight can also be seen as functions inside class list. Let's look at foldRight first. It takes zero and an operation and it returns the result type of zero or of the operation, which is a type parameter, which we didn't write here. So the U here goes in the brackets. That was a typo in the slide. So the implementation of foldRight would be we match on the current lists. That's this, the list itself. If it's Nil, then we return to zero. If it's not Nil, then we perform the operation with the first element of the list and the foldRight of the rest of the list with the same zero and the same operation. And reduceRight is essentially like foldRight, but only defined on non empty lists. So it gives an unsupported operation exception for the empty list. If the list consists of a single element, then that element as the result. If the list consists of more than one element, then it does the operation with the first element and a reduceRight of the remaining elements. So does it make a difference whether you use foldReft or foldRight or reduceLeft or reduceRight? Well, if the operator op is associative and communicative then foldLeft and foldRight are equivalent. Even though there might be a difference in efficiency, often foldLeft, for instance, can be implemented in a terror recursive way. Whereas foldRight is not tear recursive, so it needs more stack space. But sometimes only one of the two operators is appropriate. So you can find it out yourself. Here's another formulation of concat. So we can say concat of two lists xs and ys is the first list xs foldRight with zero wires and the cons operation as an operation. We can visualize the operation of concat by just drawing this operation with the diagram. So we have the foldRight that would be here the cons operation as the operation. So it takes elements x1 to xn one after another and the zero then it's the list wires. So let me just write out the list wires. What would wires look like. Well, that's what it would look like. It is y as 1, y1 to yn and nil. So we we see indeed this version of comcat neatly aligns all the elements of x, followed by all the elements of y, followed by nil, which is exactly what we want, okay? So could we have also used foldLeft instead of foldRight for this? The answer is no. And the question to you is why. Is it because the types wouldn't work out? Or the resulting function wouldn't terminate? Or the result would appear in reversed order? I invite you to copy the function concat in the worksheet, verify that it works correctly, and then replace foldRight with foldLeft. What you'll find is that the types would not work out. Indeed, we can again visualize that. If we do a foldLeft, then we would have as a zero the list x1 to xn. And then we would essentially apply the same cons operation on y1 to yn. But that's not type correct because here we're mixing a list with singular elements for the wire y1 and yn. So the two don't have the same type and that's y it doesn't work out. So remember that some sessions back we wrote the definition of reverse, which was not very performant. It had quadratic complexity where linear is what we want to achieve. We'll now fix that problem. So we develop a function for reversing lists which has a linear cost. The idea is to use the operation foldLeft for this. So our template will look like this. The reverse function, it takes a list, returns a list and its body is xs.foldLeft with sum zero, which we have to determine in some operation, which also leaves to be determined. So all we need to do is replace the zero question mark and the operation question mark. Now we could guess what they are. But the interesting bit here is that we also can compute what they are. We can try to compute zero and operation from examples. To compute that let's consider reverse of Nil. We know reverse of Nil is Nil. So we can compute as follows, Nil is reverse of Nil. Now we plug in the template of reverse so that would be Nil foldLeft sum operations z and op. And now we plug in the definition of foldLeft where we say Nil foldLeft is z. So foldLeft of the empty list is always zero, so that gives us z. And consequently because all these are equalities z equals Nil. So we still need to compute op question mark. To do that, let's plug in the next simplest list after Nil into our equation for reverse. So we start with List(x) and we know that's the same as reverse of list(x). Reversing a singleton list gives the list itself. Now we plug in our template so that would be list(x).foldLeft.Nil.op. And now we apply the definition of foldLeft. So on a single element list, foldLeft is the zero and the element combined with the operation. So that means operation of Nil and x is the same as a list(x). And list(x) is of course X colon Nil. So that suggests we should take for the operation the cons operator, but with its operands swapped. So here we have the Nil on the left hand side and X on the right hand side and here they are in the other order. So putting everything together, we get this definition of reverse. So reverse is a foldLeft with the empty list and the operation that performs a cons on its operands, but with the operand's swap. So it takes a list and an element and it returns the element pretended in front of the list. So there's a remark that this type parameter list of T here, that's necessary because otherwise the type influence of scala will get confused. We're working on that and maybe we can improve it in a future version, but it's not easy given the type inference algorithm that scalia prize. So it's one of the cases where you actually do have to write an actual type arguments. Now, what's the complexity of this implementation of reverse? Well, let's see. We've seen the operation tree of foldLeft that's obviously linear in the length of list xs. It goes once through list xs and it combines all elements with one operation. And here it's just a foldLeft with an operation which is constant time. So the answer is it's still linear in the list xs. Linear instead of quadratic, so we have achieved our objective. So here's an exercise for you. We have two functions that you know by now. That's a map function and the length function. I just wrote them outside of list as functions like this. Can you implement them in terms of foldRight? So I've already given you the outlines of other mapFun function. We would do a foldRight on the empty list because map on the empty list gives the empty list and an operation that we have to fill in. And the length function would give you a foldRight with 0 for the empty list. And again, an operation that you have to fill in. So let's see. For the map function, what we need to do if we have a foldRight. So we get an element and the remainder of the list and we do apply the function to the element and we do a cons with the remainder of the list. If we show that together with foldRight in a diagram, it would look like this. So it would be f(x1) F(xn) NiR, which is exactly what you would expect for the map function. So what about the length function? The length function can also be expressed with foldRight here. The operation we need to use is the operation that takes a list element and the length that was computed so far on the right, and just returns the length plus 1. So the element and the contents of the elements are ignored here.