我正在从“了解Haskell,以求好运!”中了解折痕。由Miran Lipovaca制作。
对于以下使用foldl
的示例:
sum' :: (Num a) => [a] -> a
sum' xs = foldl (\acc x -> acc + x) 0 xs
ghci> sum' [3,5,2,1]
11
我知道acc
是累加器,x
是起始值(列表xs
中的第一个值)。我不太了解如何将0和xs作为参数传递给lambda函数-函数如何知道acc
的值为0而x
的值为3?任何见解都会受到赞赏。
答案 0 :(得分:2)
回顾foldl
的定义:
foldl f acc [] = acc
foldl f acc (x:xs) = foldl f (f acc x) xs
现在,了解折痕的最佳方法是遍历评估。因此,让我们开始:
sum [3,5,2,1]
== foldl (\acc x -> acc + x) 0 [3,5,2,1]
foldl
函数定义的第二行表示这等效于以下内容:
== foldl (\acc x -> acc + x) ((\acc x -> acc + x) 0 3) [5,2,1]
现在,由于将lambda表达式应用于参数,因此0
和3
以acc
和x
的形式传入:
== foldl (\acc x -> acc + x) (0+3) [5,2,1]
然后重复该过程:
== foldl (\acc x -> acc + x) ((\acc x -> acc + x) (0+3) 5) [2,1]
== foldl (\acc x -> acc + x) ((0+3)+5) [2,1]
== foldl (\acc x -> acc + x) ((\acc x -> acc + x) ((0+3)+5) 2) [1]
== foldl (\acc x -> acc + x) (((0+3)+5)+2) [1]
== foldl (\acc x -> acc + x) ((\acc x -> acc + x) (((0+3)+5)+2) 1) []
== foldl (\acc x -> acc + x) ((((0+3)+5)+2)+1) []
这时,根据foldl
定义的第一行继续评估:
== ((((0+3)+5)+2)+1)
因此,直接回答您的问题:该函数知道acc
和x
的值仅仅是因为foldl
的定义将它们的值作为参数传递给该函数。
答案 1 :(得分:1)
看看如何定义foldl
函数是有帮助的:
foldl :: (b -> a -> b) -> b -> [a] -> b
foldl f a [] = a
foldl f a (x:xs) = foldl f (f a x) xs
因此,如果输入列表为空,则仅返回累加器值a
。但是,如果它不为空,则循环。在循环中,我们将累加器值更新为f a x
(即,将lambda函数f
应用于当前累加器值和列表的当前元素)。结果是新的累加器值。
我们还通过删除列表中的第一个元素来更新列表中的值(因为我们刚刚处理了第一个元素)。我们将继续处理列表中的剩余元素,直到没有剩余元素为止,这时我们将返回累加器的值。
foldl
函数等效于命令式语言中的for循环。例如,以下是我们如何在JavaScript中实现foldl
的方法:
const result = foldl((acc, x) => acc + x, 0, [3,5,2,1]);
console.log(result);
function foldl(f, a, xs) {
for (const x of xs) a = f(a, x);
return a;
}
希望阐明foldl
功能。