一些解释是为了命令!
id函数有什么作用?扮演什么角色?为什么在这里需要它?
id是恒等函数,id x = x以及建立与功能的链时被用作等效的零功能组合物,(.)。您可以在Prelude中找到它的定义。
在上面的示例中,id函数是lambda函数中的累加器吗?
累加器是通过重复功能应用程序建立的功能。由于我们将累加器命名为,所以没有显式的lambda step。如果需要,可以使用lambda编写它:
foldl f a bs = foldr (\b g x -> g (f x b)) id bs a
或者像格雷厄姆·赫顿(Graham Hutton)那样写道:
5.1foldl操作员
现在,让我们从suml示例中进行概括,并考虑foldl通过使用f组合值和以值v作为起始值的函数从左至右顺序处理列表元素的标准运算符:
foldl :: (β → α → β) → β → ([α] → β)
foldl f v [ ] = v
foldl f v (x : xs) = foldl f (f v x) xs
使用此运算符,suml可以简单地通过重新定义suml = foldl (+) 0。使用可以通过简单的方式定义许多其他功能foldl。例如,reverse可以使用foldl以下方法重新定义标准函数:
reverse :: [α] → [α]
reverse = foldl (λxs x → x : xs) [ ]
此定义比我们最初使用fold的定义更有效,因为它避免了将低效的append运算符(++)用于列表。
在上一节的功能在计算的简单概括suml显示了如何重新定义函数foldl在以下方面fold:
foldl f v xs = fold (λx g → (λa → g (f a x))) id xs v
相反,它是不可能重新定义fold来讲foldl,由于这样的事实,
foldl在其列表参数的尾部严格,但fold并非如此。还有关于一些有用的“对偶定理”fold和foldl,也是决定哪些操作是最适合于特定的应用程序(伯德,1998年)的一些准则。
foldr的原型是folder ::(a-> b-> b)-> b-> [a]-> b
哈斯克尔程序员会说型的foldr是(a -> b -> b) -> b -> [a] -> b。
第一个参数是需要两个参数的函数,但是myFoldl的实现中的step函数使用了3个参数,我完全困惑
这是令人困惑和神奇的!我们玩弄一个小技巧,并用一个函数替换累加器,然后将其应用到初始值以产生结果。
格雷厄姆赫顿解释的伎俩转foldl成foldr以上文章。我们首先记录以下内容的递归定义foldl:
foldl :: (a -> b -> a) -> a -> [b] -> a
foldl f v [] = v
foldl f v (x : xs) = foldl f (f v x) xs
然后通过对以下内容的静态参数转换来重构它f:
foldl :: (a -> b -> a) -> a -> [b] -> a
foldl f v xs = g xs v
where
g [] v = v
g (x:xs) v = g xs (f v x)
现在让我们重写g以便v向内浮动:
foldl f v xs = g xs v
where
g [] = \v -> v
g (x:xs) = \v -> g xs (f v x)
这与g将一个参数视为函数一样,它返回一个函数:
foldl f v xs = g xs v
where
g [] = id
g (x:xs) = \v -> g xs (f v x)
现在我们有了g一个递归遍历列表的函数,应用了一些函数f。最终值是恒等函数,每个步骤也将产生一个函数。
但是,我们已经在列表上有了一个非常相似的递归函数foldr!
2折叠运算符
该fold运营商有其递归论(克莱尼,1952年)的起源,而采用fold作为编程语言日期的核心概念回APL的减少运营商(艾弗森,1962年),后来到FP的插入操作符(巴克斯(1978年)。在Haskell中,fold列表的运算符可以定义如下:
fold :: (α → β → β) → β → ([α] → β)
fold f v [ ] = v
fold f v (x : xs) = f x (fold f v xs)
即,给定的函数f类型的α → β → β和值v类型β,函数
fold f v处理类型的列表[α],得到类型的值β通过更换零构造[]在列表由所述值的末尾v,并且每个缺点的构造(:)通过在列表中功能f。以这种方式,fold运算符封装了用于处理列表的简单递归模式,其中列表的两个构造函数被其他值和函数简单地替换。列表上许多熟悉的函数使用定义了一个简单的符号fold。
这看起来与我们的g函数非常类似的递归方案。现在的诀窍是:使用手头上所有可用的魔术(也称为Bird,Meertens和Malcolm),我们应用一条特殊规则,即fold的通用属性,它是g处理列表的函数的两个定义之间的等价关系,表示为:
g [] = v
g (x:xs) = f x (g xs)
当且仅当
g = fold f v
因此,folds的通用属性指出:
g = foldr k v
g对于k和,其中必须等于两个方程式v:
g [] = v
g (x:xs) = k x (g xs)
从我们早期的折叠设计中,我们知道v == id。但是对于第二个方程,我们需要计算的定义k:
g (x:xs) = k x (g xs)
<=> g (x:xs) v = k x (g xs) v
<=> g xs (f v x) = k x (g xs) v
<= g' (f v x) = k x g' v
<=> k = \x g' -> (\a -> g' (f v x))
其中,代我们计算的定义k,并v得到与foldl作为一个定义:
foldl :: (a -> b -> a) -> a -> [b] -> a
foldl f v xs =
foldr
(\x g -> (\a -> g (f v x)))
id
xs
v
递归g被folder组合器代替,累加器成为通过f列表中每个元素的链组成的功能相反的顺序构建的函数(因此,我们向左折叠而不是向右折叠)。
这肯定有点先进,因此要深入了解这种转换(即folds的通用属性),使转换成为可能,我建议使用Hutton的教程,链接如下。
参考文献
step = curry $ uncurry (&) <<< (flip f) *** (.)