向我介绍了在定义函数中使用fold的方法。我有一个想法,但我不确定为什么要这么做。对我来说,感觉就像是简化数据类型和数据值的名称一样。如果可以向我展示一些使用fold的示例,那就太好了。
data List a = Empty | (:-:) a (List a)
--Define elements
List a :: *
[] :: List a
(:) :: a -> List a -> List a
foldrList :: (a -> b -> b) -> b -> List a -> b
foldrList f e Empty = e
foldrList f e (x:-:xs) = f x (foldrList f e xs)
答案 0 :(得分:2)
折叠的想法很强大。折叠函数(Haskell基础库中的foldr
和foldl
)来自称为高阶函数的函数族(对于那些不知道的人-这些函数将函数作为参数或返回函数作为其输出)。
由于可以更清楚地表达程序的意图,因此可以使代码更清晰。使用fold函数编写的函数强烈表明有意遍历该列表并重复应用该函数以获得输出。对于简单的程序,使用标准的递归方法是很好的选择,但是当复杂度增加时,可能难以快速了解正在发生的事情。
由于传递函数作为参数的性质,可以通过折叠实现更大的代码重用。如果程序的某些行为受到布尔值或枚举值的影响,则可以将该行为抽象为单独的函数。然后可以将单独的函数用作fold的参数。这样可以实现更大的灵活性和简便性(因为有2个更简单的功能与1个更复杂的功能)。
高阶函数对于Monads也是必不可少的。
对这个问题的评论也应归功于它的多样性和信息性。
答案 1 :(得分:2)
高阶函数,例如foldr
,foldl
,map
,zipWith
和&c。捕获递归的常见模式,因此可以避免手动编写递归定义。这使您的代码更高级,更易读:程序员不必单步执行代码并推断递归函数的功能,而可以推理出更高级别组件的组成。
举一个极端的例子,考虑手动递归计算标准差:
standardDeviation numbers = step1 numbers
where
-- Calculate length and sum to obtain mean
step1 = loop 0 0
where
loop count sum (x : xs) = loop (count + 1) (sum + x) xs
loop count sum [] = step2 sum count numbers
-- Calculate squared differences with mean
step2 sum count = loop []
where
loop diffs (x : xs) = loop ((x - (sum / count)) ^ 2 : diffs) xs
loop diffs [] = step3 count diffs
-- Calculate final total and return square root
step3 count = loop 0
where
loop total (x : xs) = loop (total + x) xs
loop total [] = sqrt (total / count)
(为了公平起见,我还对求和进行内联,因此有点过分,但这大致就是通常用命令式语言(手动循环)完成的方式。)
现在考虑使用对标准函数的调用组成的版本,其中一些是高阶的:
standardDeviation numbers -- The standard deviation
= sqrt -- is the square root
. mean -- of the mean
. map (^ 2) -- of the squares
. map (subtract -- of the differences
(mean numbers)) -- with the mean
$ numbers -- of the input numbers
where -- where
mean xs -- the mean
= sum xs -- is the sum
/ fromIntegral (length xs) -- over the length.
我希望这种更具说明性的代码也更具可读性-无需过多注释,就可以用两行代码整齐地编写。显然,它比低级递归版本更正确。
此外,sum
,map
和length
都可以用折叠以及许多其他标准功能(例如product
,{{1})来实现},and
,or
等。折叠不仅对列表而且对各种容器(参见concat
类型类)都是极为普遍的操作,因为它捕获了从容器的所有元素中递增计算的模式。
使用折叠而不是手动递归的最后一个原因是性能:由于懒惰和优化,GHC在使用基于Foldable
的函数时知道如何执行操作,因此编译器可能会融合一系列折叠(地图, &c。)一起在运行时合并成一个循环。