Haskell “的身份”;累积参数";foldr函数的定义
foldr功能:Haskell “的身份”;累积参数";foldr函数的定义,haskell,Haskell,foldr功能: foldr :: (a -> b -> b) -> b -> [a] -> b foldr func acc [] = acc foldr func acc (x:xs) = func x (foldr func acc xs) 捕捉像这样的图案(左侧) 并使它们更简单(右侧) 我注意到的一件事是acc参数,作为折叠的输入, 似乎正是该函数的中性元素/标识元素 In Mathematics the neutral element of
foldr :: (a -> b -> b) -> b -> [a] -> b
foldr func acc [] = acc
foldr func acc (x:xs) = func x (foldr func acc xs)
捕捉像这样的图案(左侧)
并使它们更简单(右侧)
我注意到的一件事是acc参数,作为折叠的输入,
似乎正是该函数的中性元素/标识元素
In Mathematics the neutral element of the addition operation + is 0
because n + 0 = n, n ∈ ℝ
它不会改变任何东西,换句话说:
将此中性元素作为加法函数的输入,求和等于和
(+)summand 0=summand
或summand+0=summand
乘法也是一样,因子和恒等式的乘积等于因子itelf:
(*)系数1=系数
那么这只是巧合还是背后有更大的原因?你说得完全正确。我们经常希望将一个类似于“identity”的元素传递给foldr
,这样“起点”就不会影响结果。事实上,这是在Haskell中用Monoid
typeclass编码的。A是具有标识的关联二进制操作。您提供的示例都是幺半群的示例,它们都存在于Haskell中
- 任何
Num
上的+
被编码为上的幺半群
- 任何
Num
上的*
都被编码为上的幺半群
- 任何列表上的
++
都被编码为[a]
上的幺半群
事实上,我们可以更进一步。对幺半群进行折叠是一种常见的做法,我们可以使用(或者,如果您需要消除歧义的话)自动进行折叠。比如说,
import Data.Foldable
import Data.Monoid
sum :: Num a => [a] -> a
sum = getSum . foldMap Sum
product :: Num a => [a] -> a
product = getProduct . foldMap Product
concat :: [[a]] -> [a]
concat = fold
如果您在源代码中查找Foldable
,可以看到fold
和foldMap
实际上是根据monoid上的foldr
定义的,因此这与您刚才描述的完全相同
您可以在Hackage上找到(内置)Monoid
实例的完整列表,但您可能会发现一些其他有趣的实例:
- 布尔上的
|
是一个带
- 布尔函数上的
&&
是一个带的幺半群
- 函数合成是一个带的幺半群(简称“自同态”)
作为一个练习,你可以试着精确地确定每一个操作的身份。< /P>你的翻译表实际上是错误的。您不需要区分
[]
和(x:xs)
大小写,只需要一个子句sum xs=foldr(+)0 xs
(此外,您使用了foldl'
而不是foldr
,但这更多是一个性能细节)。这不是巧合。你需要一些“中性”的初始元素来开始。但是没有什么可以阻止您编写sumPlus5=foldr 5这样的代码。Product
的mempty
元素(即乘法的标识参数)是1
,而不是0
。
import Data.Foldable
import Data.Monoid
sum :: Num a => [a] -> a
sum = getSum . foldMap Sum
product :: Num a => [a] -> a
product = getProduct . foldMap Product
concat :: [[a]] -> [a]
concat = fold