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
/foldLeft
as 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
foldLeft
is a method with 3 parametersA
andB
are type parametersxs
is 1st parameter of the method, of typeList[A]
e
is 2nd parameter, of typeB
f
is 3rd parameter, of type(B, A) => B
- the type
(B, A) => B
represents a function taking two parameters of typeB
andA
respectively, and returning aB
- the type
- finally,
B
is the return type of the method