Haskell:了解foldl函数

时间:2019-06-09 01:42:40

标签: haskell functional-programming

我正在从“了解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?任何见解都会受到赞赏。

2 个答案:

答案 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表达式应用于参数,因此03accx的形式传入:

== 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)

因此,直接回答您的问题:该函数知道accx的值仅仅是因为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功能。