I am confused about what this means. I understand currying but I can't seem to read the code altogether.
def foldLeft [A,B](xs:List[A], e:B, f:(B,A)=>B): B
CodePudding user response:
Just a couple of advices.
By the way, there is no currying in
def foldLeft[A,B](xs: List[A], e: B, f: (B, A) => B): B
- You can think of
foldRight/foldLeftas a just short way to describe a recursion (instead of pattern matching a list and making recursive calls).
Let's consider an example. Let's have some recursion. For example let's have a wrapper class
case class Value[A](value: A)
And let's transform a list of Value[A] into a value wrapping a list of A i.e. List[Value[A]] into Value[List[A]]. For example we'd like to transform List(Value(1), Value(2), Value(3)) into Value(List(1, 2, 3)). Surely, we could do this with .map but let's pretend that we don't know map (it shouldn't be surprising that we can do mapping because map can be expressed via foldRight/foldLeft).
We can do this recursively in two ways (at least). Either simple recursion
def valuesToValue[A](values: List[Value[A]]): Value[List[A]] = values match {
case Nil => Value(Nil)
case v :: vs => Value(v.value :: valuesToValue(vs).value)
}
or tail recursion with a helper function and accumulator
def valuesToValue[A](values: List[Value[A]]): Value[List[A]] = {
@tailrec
def loop(values: List[Value[A]], acc: Value[List[A]]): Value[List[A]] = values match {
case Nil => Value(acc.value.reverse)
case v :: vs => loop(vs, Value(v.value :: acc.value))
}
loop(values)(Value(Nil))
}
Very simple. Just wrapping-unwrapping.
Both recursive implementations of valuesToValue can be (automatically) re-written with foldRight/foldLeft.
The former recursion can be re-written with foldRight. The latter recursion (tail one) can be re-written with foldLeft.
Let's re-write the 1st recursion with foldRight. The value from branch case Nil => ... becomes the 1st argument of foldRight. The value from branch case v :: vs => ... becomes the 2nd argument of foldRight if we replace the result of recursive call valuesToValue(vs) with a new variable res, so in the 2nd argument of foldRight we'll have a function of v: Value[A] and res: Value[List[A]]
def valuesToValue[A](values: List[Value[A]]): Value[List[A]] =
values.foldRight( Value(Nil: List[A]) ){
(v, res) => Value(v.value :: res.value)
}
Let's re-write the 2nd recursion (tail one) with foldLeft.
First of all, let's recall that because of currying we can understand the helper loop as a single-parameter function into Value[List[A]] => Value[List[A]]
def loop(values: List[Value[A]]): Value[List[A]] => Value[List[A]] = values match {
case Nil => acc => Value(acc.value.reverse)
case v :: vs => acc => loop(vs)(Value(v.value :: acc.value))
}
Now the value from branch case Nil => ... becomes the 1st argument of foldLeft. The value from branch case v :: vs => ... becomes the 2nd argument of foldLeft if we replace the result of recursive call loop(vs)(_) with a new variable res (a function)
def valuesToValue[A](values: List[Value[A]]): Value[List[A]] = {
def loop(values: List[Value[A]]): Value[List[A]] => Value[List[A]] =
values.foldRight[Value[List[A]] => Value[List[A]]](
acc => Value(acc.value.reverse)
)(
(v, res) => acc => res(Value(v.value :: acc.value))
)
loop(values)(Value(Nil))
}
So, roughly speaking, values from branches case Nil => ... and case v :: vs => ... become arguments of foldRight/foldLeft depending on whether we calculate with simple recursion or tail recursion with accumulator.
CodePudding user response:
Let's dive into this method signature:
foldLeft[A,B](xs: List[A], e: B, f: (B, A) => B): B
foldLeftis a method with 3 parametersAandBare type parametersxsis 1st parameter of the method, of typeList[A]eis 2nd parameter, of typeBfis 3rd parameter, of type(B, A) => B- the type
(B, A) => Brepresents a function taking two parameters of typeBandArespectively, and returning aB
- the type
- finally,
Bis the return type of the method


