我刚刚做了这个练习,并想分享我是如何得出答案的(与问题中的内容基本相同,只是字母不同),希望它对某人有用。
作为背景,让我们从做什么foldLeft
和foldRight
做什么开始。例如,对列表 [1, 2, 3] 进行 foldLeft 的结果与操作*
和起始值z
为该值
((z * 1) * 2) * 3
我们可以将 foldLeft 视为从左到右递增地使用列表的值。换句话说,我们最初从值开始z
(如果列表为空,结果将是什么),然后我们揭示foldLeft
我们的列表从 1 开始并且值变为z * 1
,然后foldLeft
看到我们的列表下一个具有2
并且值变为(z * 1) * 2
,最后,作用于 3 后,变为值((z * 1) * 2) * 3
。
1 2 3
Initially: z
After consuming 1: (z * 1)
After consuming 2: ((z * 1) * 2
After consuming 3: (((z * 1) * 2) * 3
这个最终值是我们想要达到的值,除非(如练习要求我们)使用foldRight
代替。现在请注意,就像foldLeft
从左到右使用列表的foldRight
值一样,从右到左使用列表的值。所以在列表 [1, 2, 3] 上,
- 这个 foldRight 将作用于 3 和 [something],给出 [result]
- 然后它将作用于 2 和 [result],给出 [result2]
- 最后它将作用于 1 和 [result2] 给出最终表达式
- 我们希望我们的最终表达式是
(((z * 1) * 2) * 3
换句话说:使用foldRight
,我们首先得到如果列表为空的结果是什么,然后是列表只包含 [3] 的结果,然后是列表为 [2, 3] 的结果,最后是结果列表为 [1, 2, 3]。
也就是说,这些是我们想要达到的值,使用foldRight
:
1 2 3
Initially: z
After consuming 3: z * 3
After consuming 2: (z * 2) * 3
After consuming 1: ((z * 1) * 2) * 3
所以我们需要从z
to(z * 3)
到(z * 2) * 3
to ((z * 1) * 2) * 3
。
作为values,我们不能这样做:对于任意操作,从 value(z * 3)
到 value没有自然的方式。(有乘法,因为它是可交换的和关联的,但我们只是用来代表任意操作。)(z * 2) * 3
*
*
但作为函数,我们也许可以做到这一点!我们需要有一个带有“占位符”或“洞”的功能:可以z
把它放在适当的位置。
- 例如,在第一步之后(在作用于 3 之后)我们有占位符函数
z => (z * 3)
。或者更确切地说,作为一个函数必须采用任意值并且我们一直在使用z
一个特定的值,让我们把它写成t => (t * 3)
. (应用于输入的这个函数z
给出了值(z * 3)
。)
- 在第二步之后(在作用于 2 和结果之后)我们可能有占位符函数
t => (t * 2) * 3
?
我们可以从第一个占位符函数转到下一个吗?让
f1(t) = t * 3
and f2(t) = (t * 2) * 3
f2
是什么f1
?
f2(t) = f1(t * 2)
我们可以!所以我们想要的函数接受2
和f1
给出f2
。让我们称之为g
. 我们有g(2, f1) = f2
哪里f2(t) = f1(t * 2)
或换句话说
g(2, f1) =
t => f1(t * 2)
让我们看看如果我们继续这样做是否可行:下一步将是g(1, f2) = (t => f2(t * 1))
RHS 与t => f1((t * 1) * 2))
or相同t => (((t * 1) * 2) * 3)
。
看起来它有效!最后我们应用z
到这个结果。
第一步应该是什么?我们应用g
on3
和f0
to get f1
,其中f1(t) = t * 3
定义如上,也f1(t) = f0(t * 3)
来自定义g
。所以看起来我们需要f0
成为身份函数。
让我们重新开始。
Our foldLeft(List(1, 2, 3), z)(*) is ((z * 1) * 2) * 3
Types here: List(1, 2, 3) is type List[A]
z is of type B
* is of type (B, A) -> B
Result is of type B
We want to express that in terms of foldRight
As above:
f0 = identity. f0(t) = t.
f1 = g(3, f0). So f1(t) = f0(t * 3) = t * 3
f2 = g(2, f1). So f2(t) = f1(t * 2) = (t * 2) * 3
f3 = g(1, f2). So f3(t) = f2(t * 1) = ((t * 1) * 2) * 3
最后我们在 z 上应用 f3 并得到我们想要的表达式。一切正常。所以
f3 = g(1, g(2, g(3, f0)))
这意味着 f3 =foldRight(xs, f0)(g)
让我们定义g
,这次不是x * y
使用任意函数s(x, y)
:
把这一切放在一起
def foldLeft[A, B](xs: List[A], z: B)(s: (B, A) => B): B = {
val f0 = (b: B) => b
def g(a: A, f: B=>B): B=>B =
t => f(s(t, a))
foldRight(xs, f0)(g)(z)
}
在阅读本书的这个级别上,我实际上更喜欢这种形式,因为它更明确且更容易理解。但是为了更接近解决方案的形式,我们可以内联和的定义f0
(g
我们不再需要在输入时声明类型g
并且foldRight
编译器推断它),给出:
def foldLeft[A, B](xs: List[A], z: B)(s: (B, A) => B): B =
foldRight(xs, (b: B) => b)((a, f) => t => f(s(t, a)))(z)
这正是问题所在,只是使用了不同的符号。就 foldLeft 而言,对于 foldRight 也是如此。