【问题标题】:Identity of the "accumulating parameter" of the foldr functionfoldr 函数的“累积参数”的标识
【发布时间】:2018-08-01 04:28:26
【问题描述】:

foldr 功能:

foldr :: (a -> b -> b) -> b -> [a] -> b
foldr func acc []      = acc
foldr func acc (x:xs)  = func x (foldr func acc xs)

捕捉类似的模式(左侧) 并使它们更简单(右侧)

sum :: [Integer] -> Integer       |  sum :: [Integer] -> Integer
sum [] = 0                        |  sum [] = 0
sum (x:xs) = x + sum xs           |  sum (x:xs) = foldr (+) 0 xs
                                  |
product :: [Integer] -> Integer   |  product :: [Integer] -> Integer
product [] = 0                    |  product [] = 0
product (x:xs) = x * product xs   |  product (x:xs) = foldr (*) 1 xs
                                  |
concat :: [[a]] -> [a]            |  concat :: [[a]] -> [a]
concat [] = []                    |  concat [] = []
concat (x:xs) = x ++ concat xs    |  concat (x:xs) = foldr (++) [] xs
----------------------------------------------------------------------
       not using folds            |           using folds 

我注意到的一件事是 acc 参数作为折叠的输入提供, 似乎正是该功能的中性元素/身份元素。

In Mathematics the neutral element of the addition operation + is 0
because n + 0 = n, n ∈ ℝ

它不会改变任何东西,换句话说: 将此中性元素作为加法函数的输入提供后,被加数等于和。

(+) summand 0 = summandsummand + 0 = summand

乘法也是如此,因式和恒等式的乘积等于因式itelf:

(*) factor 1 = factor

所以这只是巧合还是背后有更大的原因?

【问题讨论】:

  • 您的翻译表实际上是错误的。您不需要 [](x:xs) 区分大小写,只需一个子句 sum xs = foldr (+) 0 xs
  • (另外,您使用了foldl' 而不是foldr,但这更多的是性能细节。)
  • 这不是巧合。你需要一些“中性”的初始元素来开始。但是没有什么能阻止你写 sumPlus5 = foldr 5
  • 另外,Product(即乘法的恒等参数)的 mempty 元素是 1,而不是 0

标签: haskell


【解决方案1】:

你完全正确。我们经常想将一个类似“身份”的元素传递给foldr,这样“起点”根本不会影响结果。事实上,这在 Haskell 中被编码为 Monoid 类型类。 monoid 是具有标识的关联二元运算。你提供的例子都是幺半群的例子,它们都存在于 Haskell 中。

  • 任何Num 上的+ 都被编码为Sum newtype 上的幺半群。
  • 任何Num 上的* 都被编码为Product newtype 上的幺半群。
  • 任何列表中的++ 都被编码为[a] 上的一个幺半群。

事实上,我们可以更进一步。折叠一个幺半群是一种常见的做法,我们可以使用fold(或foldMap,如果您需要消除歧义)自动完成。例如,

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 的源代码,您会看到foldfoldMap 实际上是根据幺半群上的foldr 定义的,所以这与您刚才描述的完全相同。

您可以在 Hackage 上找到(内置)Monoid 实例的完整列表,但您可能会感兴趣的其他一些实例:

  • 布尔值上的|| 是带有Any newtype 的幺半群。
  • 布尔值上的&& 是一个带有All newtype 的幺半群。
  • 函数组合是一个带有Endo newtype(“endomorphism”的缩写)的幺半群

作为练习,您可以考虑尝试确定每个操作的身份。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-05-13
    • 1970-01-01
    • 2020-11-26
    • 1970-01-01
    • 2019-07-23
    • 2013-01-28
    • 2012-07-18
    • 2012-05-31
    相关资源
    最近更新 更多