Haskell “的身份”;累积参数";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功能:

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