与大多数其他语言相比,Haskell 中的类型更重要且信息量更大。当您不了解 Haskell 时,最好的第一步是考虑类型。所以让我们这样做。我们将启动 ghci 并输入:
Prelude> let mult_add d s = d + 10 * s
现在询问它的类型:
Prelude> :t mult_add
mult_add :: Num a => a -> a -> a
也就是说, mult_add 接受 ana
和 another a
,并返回 an a
,附带条件a
是类的实例Num
(以便您可以将它们相加和相乘)。
你被要求使用foldr
来编写这个函数,所以让我们看看它的类型:
Prelude> :t foldr
foldr :: (a -> b -> b) -> b -> [a] -> b
这看起来有点吓人,所以让我们分解一下。第一部分,(a -> b -> b)
告诉我们foldr
需要两个变量的函数,a
和b
。好吧,我们已经有了其中之一 - 它是mult_add
. 那么如果我们mult_add
作为第一个参数输入会发生foldr
什么呢?
Prelude> :t foldr mult_add
foldr mult_add :: Num b => b -> [b] -> b
好的!我们现在有一个函数,它接受 ab
和 a [b]
(b
s 的列表)并返回 a b
。您尝试编写的函数需要在0
给定空列表时返回,因此让我们尝试将空列表提供给它,并为其余参数提供一些不同的值:
Prelude> foldr mult_add 10 []
10
Prelude> foldr mult_add 5 []
5
嘿,这很有趣。如果我们给它输入数字x
和空列表,它只会返回x
(注意:这总是正确的foldr
。如果我们给它初始值x
和空列表[]
,它会返回x
,无论我们使用什么函数代替mult_add
。)
因此,让我们尝试将其0
作为第二个参数提供:
Prelude> foldr mult_add 0 []
0
这似乎行得通。现在如果我们给它提供列表[1,2,3,4]
而不是空列表呢?
Prelude> foldr mult_add 0 [1,2,3,4]
4321
好的!所以它似乎工作。现在的问题是,为什么它会起作用?理解的诀窍foldr
是在 的每个元素之间foldr f x xs
插入函数,另外放在 列表的末尾,并从右侧收集所有内容(这就是为什么它被称为右折叠)。因此,例如:f
xs
x
foldr f 0 [1,2,3] = 1 `f` (2 `f` (3 `f` 0))
其中反引号表示我们正在使用中缀形式的函数(所以它的第一个参数是左边的那个,第二个参数是右边的那个)。在您的示例中f = mult_add
,您有 将其第二个参数乘以 10 并将其添加到第一个参数:
d `mult_add` s = d + 10 * s
所以你有了
foldr mult_add 0 [1,2,3] = 1 `mult_add` (2 `mult_add` (3 `mult_add 0))
= 1 `mult_add` (2 `mult_add` 3)
= 1 `mult_add` 32
= 321
这符合您的期望。为了确保您理解这一点,请弄清楚如果您mult_add
以相反的方式定义会发生什么,即
mult_add d s = 10 * d + s