2 回答
我会说首先运行以下,一一,在GHCi
:
:t foldr
:info foldr
:doc foldr
:t foldl
:info foldl
:doc foldl
:t map
:info map
:doc map
或者更好的是,打开hoogle.haskell.org并搜索上述每个功能,然后单击第一个链接。
但我同意 Haskell 文档很难阅读,尤其是对于初学者。我是初学者,阅读和理解它们有很多困难。
这是一个使用map
并foldr
显示如何foldr
工作的函数:
printFoldr xs = foldr (\x acc -> "(" ++ x ++ " + " ++ acc ++ " )") "0" $ map show xs
现在运行看这个:
printFoldr [1..5]
-- outputs the following:
"(1 + (2 + (3 + (4 + (5 + 0 ) ) ) ) )"
这向我们展示了如何foldr
评估。在讨论如何foldr
评估之前,让我们简要地看一下map
.
map show [1..5]
-- outputs the following:
["1","2","3","4","5"]
这意味着map
需要 2 个参数。一个列表和一个应用于列表中每个元素的函数。结果是一个新列表,其中函数应用于每个元素。因此,应用于show
每个数字会输出它们的字符串表示形式。
回到foldr
。foldr
接受 3 个参数:
- 类型函数
a -> b -> b
- 类型的初始值
b
- 类型列表
[a]
foldr
获取所提供列表的每个值并将此函数应用于它。特殊之处在于 map 在每次迭代中保留函数的输出,并在下次运行时将其作为第二个参数传递给函数。因此可以方便地编写foldr
如下传递的函数:(\el acc -> do something)
. 现在在 的下一次迭代中foldr
,acc
将保存上一次运行的值,el
并将成为列表中的当前元素。BTW,acc
代表累加器和el
元素。这使我们能够将所提供列表的元素减少为全新的东西。
正如您在 中看到的printFoldr
,初始值只是一个空字符串,但它会逐渐将列表元素添加到其中,显示它将如何将列表中的元素减少到它们的总和。
这是一个想法:
a1-a2+a3-a4+...
=
a1-(a2-(a3-(a4-(...(an-0)...))))
这非常适合foldr
递归的模式,
foldr f z [a1,a2,a3,a4,...,an]
=
a1`f`(a2`f`(a3`f`(a4`f`(...(an`f`z)...))))
所以可以通过设置f = ...
和z = ...
调用来编码
asum :: (Num a) => [a] -> a
asum xs = foldr f z xs
where
f = (...)
z = (...)
您将需要完成此定义。
对于XOR
布尔值列表的 ,假设True
如果其中一个且只有一个为 ,则为True
,False
否则,我们可以想象以下转换序列:
[ True, False, False, True, True, False, ...]
==>
[ t, f, f, t, t, f, ...]
其中t
和f
是一些特别选择的数字。然后我们可以找到第二个列表的总和(不是交替总和,只是一个数字列表的总和)并检查它是否等于......一些(其他?)特殊数字,我们称之为n1
:
xor :: [Bool] -> Bool
xor bools = (aNumber ... n1)
where
list1 = bools
list2 = fun1 transform list1
transform False = f
transform True = t
f = ...
t = ...
aNumber = sum list2
n1 = ...
fun1 = ...
sum listOfNums = ...
fun1
是根据上面调用的给定函数转换其参数列表中每个元素的函数transform
。考虑到我们已经在使用foldr
.
sum
将通过使用剩下的最后一个函数来实现。
供参考,
map foo [a1,a2,a3,...,an]
=
[foo a1, foo a2, foo a3, ..., foo an]
和
foldl f z [a1,a2,a3,...,an]
=
((((z`f`a1)`f`a2)`f`a3)...)`f`an