我一直认为尾递归函数在以下方面更好
性能优于非尾递归版本。因此,对列表中的项目进行计数可能是这样实现的:
count:: [a] -> Int
count [] = 0
count (x:xs) = 1 + count xs
但是此函数不是尾递归的,因此性能也不尽如人意。解决方法是累积计数,如下所示:
_count:: Num b => b -> [a] -> b
_count b [] = b
_count b (x:xs) = _count (b + 1) xs
count:: [a] -> Int
count = _count 0
这可以通过尾递归折叠轻松实现:
myfold:: (b -> a -> b) -> b -> [a] -> b
myfold f b [] = b
myfold f b (x:xs) = myfold f (f b x) xs
count = myfold incr 0
where incr c _ = c + 1
但是,后来我想起了左右折的一些东西。结果是
myfold
是左折,根据Real World Haskell的说明,不应使用f b x
:...由于
myfold
应用程序的不完善因此,我尝试将
foldl
重写为正确的折叠:myfoldr:: (a -> b -> b) -> b -> [a] -> b
myfoldr f b [] = b
myfoldr f b (x:xs) = f x (myfoldr f b xs)
但这不是尾递归的。
这样看来,Haskell非严格评估会使尾递归
不太重要。但是,我有种感觉,对于列表中的项目进行计数,严格的
foldr
应该比任何Integer
都要好,因为我们无法从ojit_code提取任何内容。综上所述,我认为这些是更好的 map 和计数实现方法(使用折叠):
map:: (a -> b) -> [a] -> [b]
map f = foldr g []
where g x fxs = (f x):fxs
count:: [a] -> Int
count = foldl incr 0
where incr c _ = c + 1
这样对吗?
最佳答案
没错,并且尾部调用效率更高。但是,通过创建大型thunk的成本可以抵消此好处,而foldl
就是这种情况。
也可以吃蛋糕的方法是确保不累加累加器,而要对其进行热切评估:
myfold:: (b -> a -> b) -> b -> [a] -> b
myfold f !b [] = b
myfold f !b (x:xs) = myfold f (f b x) xs
当然,这就是
foldl'
函数。TL; DR:请勿使用
foldl
,但请使用foldl'
。