You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

能否用fold实现Haskell中的iterate函数?

能否用fold实现Haskell的iterate函数?

背景

我之前想实现一个功能:把函数f重复应用在参数z上,生成无限列表[z, f z, f (f z), f (f (f z)), ...],这样就能用take n、takeWhile这类函数去截取。

一开始我觉得要处理无限列表,想到了repeat f,觉得应该用foldr来实现,但试了之后发现走不通:对无限列表用foldr的时候,累加器一般设成undefined(因为用不上),但像foldr (\f (x:xs) -> f x:x:xs) undefined (repeat f)这种写法,根本没法把初始参数z加进去。

问题

后来准备提问时,发现Haskell里已经有iterate函数了,它的实现是这样的:

{-# NOINLINE [1] iterate #-}
iterate :: (a -> a) -> a -> [a]
iterate f x =  x : iterate f (f x)

所以现在我的问题是:能不能用fold系列函数实现iterate?如果可以,怎么实现?不行的话,原因是什么?

解答

完全可以用foldr实现iterate,核心是利用Haskell的惰性求值特性,把初始值z整合到折叠逻辑里,而不是像你之前那样孤立地操作列表结构。

具体实现

第一种思路是借助repeat f生成无限个f的列表,然后用foldr从初始值z开始,不断将函数应用到前一个元素上构建列表:

iterateViaFoldr :: (a -> a) -> a -> [a]
iterateViaFoldr f z = z : foldr (\g acc -> g (head acc) : acc) [] (repeat f)

不过这个写法有小缺陷:每次取head acc会有额外开销,而且依赖acc永远非空(这里因为是无限列表所以没问题,但不够优雅)。

更简洁高效的写法是让foldr生成一个递归函数,再把初始值z作为参数传入:

iterateViaFoldr' :: (a -> a) -> a -> [a]
iterateViaFoldr' f z = foldr (\next step x -> x : step (next x)) (const []) (repeat f) z

这里的逻辑是:foldr遍历无限的repeat f列表,每一步生成一个函数——这个函数接收当前值x,返回x拼上“把下一个函数next应用到x后,继续执行后续步骤”的结果。最后把初始值z传给这个由foldr生成的函数,就得到了目标无限列表。

你之前尝试失败的原因

你之前的写法没有把初始值z融入折叠过程,只是想直接用foldr操作列表结构,还把累加器设成了undefined,自然没法注入初始值。foldr处理无限列表的关键是让每一步计算都能惰性生成列表元素,同时把初始值作为整个折叠过程的输入,而不是放在逻辑之外。

另外补充:虽然unfoldr是实现iterate更直观的工具(iterate f z = unfoldr (\x -> Just (x, f x)) z),但既然问题聚焦在fold系列,上面的foldr实现完全符合要求。


内容的提问来源于stack exchange,提问作者Enlico

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.29 09:53:18