sum :: (Num a) => [a] -> a
sum xs = foldl (\acc x -> acc + x) 0 xs
foldl从左侧折叠列表。首先我们得到acc=0
并将列表xs放到x,然后执行函数->acc+x
。计算之后,我们得到新的acc,它等于acc+x
。但那是为什么呢?我认为acc+x
的结果是基于函数x->acc+x
的x的新值。
答案 0 :(得分:3)
你应该看看foldl的定义:
foldl f z [] = z
foldl f z (x:xs) = foldl f (f z x) xs
foldl接收一个带有2个参数的函数,一个值(“起始值”或累加器)和一个列表。 如果列表为空,则返回当前计算。 如果大小写不为空,那么它使用与函数相同的函数递归调用,累加器是使用累加器作为第一个参数调用函数的结果,列表的第一个元素作为第二个参数和尾部该列表用作递归调用的列表。
因此,sum中使用的lambda函数变得非常清楚,它将acc作为第一个参数,将list的元素作为第二个参数,并返回两者的总和。
调用的结果:
sum [1,2,3] = ((0 + 1) + 2) + 3 = 6
答案 1 :(得分:3)
让我们来看看你对sum的定义
sum :: (Num a) => [a] -> a
sum xs = foldl (\acc x -> acc + x) 0 xs
让我们来看看foldl的签名:
foldl :: (a -> b -> a) -> a -> [b] -> a
嗯,好吧,为了获得最终(->a
)的价值,我们需要为foldl提供什么?
它需要一个curried函数(a->b->a)
。虽然不准确,但为了简洁起见,我们会说它是一个带有两个参数的函数(但是你和我知道真的,它需要一个参数并返回另一个带有一个参数的函数)。
它需要a
类型的值。请注意,我们从步骤1开始的curried函数采用类型为a
的类型,并返回a
类型的内容。有趣...嗯...
它需要一个类型为b
的列表。请注意,我们从第1步中获得了curried函数,以及a
类型的某些类型b
。
那么,我们是否给它想要的东西?
我们给它(\acc x -> acc + x)
。这是一个匿名函数,或者 lambda ,它有两个参数,(记住,它是咖喱的),acc
和x
,以及返回他们的总和。
我们将0
作为起始值
我们将xs
作为要折叠的列表。
好的dokie。所以,让我们让foldl运用它的Haskell魔法。我们假设我们打电话给sum [1,2,3]
foldl
使用(\acc x -> acc + x)
0
和acc
xs
的第一个值来调用我们的函数1
。
0 + 1
此结果不会被存储在acc
或x
中,因为它们只是我们的小lambda函数中的参数。 foldl
将使用该值(有关具体实现,请参阅SanSS的答案)。
请记住,我们的lambda函数的结果与第一个参数的结果相同? foldl可以使用前一个总和并将其与第二个元素一起传递回lambda函数。
(0 + 1) + 2
再次为所有元素做到这一点:
((0 + 1) + 2) + 3
6
正如Dan指出的那样,如果您这样做,情况也是如此:
sum xs = foldl (+) 0 xs
您可以使用此功能更轻松地告诉我们,我们不只是“设置”某个变量并添加到其上。
希望这会有所帮助。
旁注:
对于sum的定义,您不必明确声明sum
需要xs
。你可以把它留作:
sum = foldl (\acc x -> acc + x) 0
这利用了currying,因为如果我们提供foldl只是它的前两个参数 - 像(a->b->a)
这样的curried函数和类型a
的值 - 我们得到了什么?
[b] -> a
一个获取类型b
列表并返回类型a
的值的函数!这称为pointfree style。需要考虑的事项: - )
答案 2 :(得分:1)
从你的问题来看,听起来你不明白lambda函数(\acc x -> acc + x)
在这里的作用。
该功能不是x->acc+x
,而是acc x->acc + x
。实际上,您可以将“sum”等式重写为
sum xs = foldl (+) 0 xs
由于(\acc x -> acc + x)
与(+)
我建议你(重新)阅读http://learnyouahaskell.com/higher-order-functions#lambdas