在与my previous question相同的情况下,当我想出以下wrong函数试图连接累加器时,我尝试仅使用折叠实现cycle
函数¹本身,以指数方式构建无限列表(是的,我知道这意味着如果要take
1025个副本,它将生成2048个副本)
myCycle :: [a] -> [a]
myCycle s = foldr (\_ a -> a ++ a) s [1..]
但是,使用它会抛出*** Exception: heap overflow
。
相反,此版本像吊饰一样工作>>
?我觉得原因是愚蠢的,然后我...myCycle :: [a] -> [a] myCycle s = foldr (\_ a -> s ++ a) s [1..]
我的问题是,为什么前一个版本会溢出,与后者相比
[[1]我的意思是,将cycle
实现为折叠,仅将阶跃函数和种子作为自由度。
[在与上一个问题相同的情况下,当我想到以下错误的函数试图将....>]串联时,我尝试使用折叠仅实现循环函数¹。
foldr c n
列出一个列表,并将每个(:)
替换为c
,最后一个[]
替换为s
。但是[1..]
没有最终的[]
,因此foldr (\_ a -> a ++ a) s
没有放置s
的位置。因此,没有信息从s
到myCycle s
的结果“流动”,这意味着它别无选择,只能是底层的(或者说:它有太多的选择,它的指定不充分,因此它放弃并退回到底部)。第二个版本实际上使用s
,因为它出现在折叠功能中,当foldr
作用于一个无限列表时,该折叠功能实际上,我们具有身份
foldr (\_ -> f) x xs = fix f = let x = f x in x
xs
为无穷大时。也就是说,当列表为无限时,[foldr
的第二个参数为
完全忽略。此外,如果该折叠功能实际上没有查看列表中的元素,那么真正发生的就是您将f
无限嵌套在其内部:fix f = f (f (f (f ...)))
。fix
是基本的,因为每种递归都可以用它来表示(某些更特殊的递归需要添加一些语言扩展,但fix f = let x = f x in x
本身不变)。这使得编写任何基于foldr
和无穷列表的递归函数都很简单。这是我的指数周期。它产生输入的1个副本,连接到2个副本,连接到4个副本,等等。
myCycle xs = xs ++ myCycle (xs ++ xs)
您通过将递归调用抽象为参数并将其传递给fix
,将显式递归定义转换为fix
:
myCycle = fix \rec xs -> xs ++ rec (xs ++ xs)
然后您使用
foldr
身份并引入了一个假冒[]
案例
myCycle = foldr (\_ rec xs -> xs ++ rec (xs ++ xs)) (error "impossible") [1..]