Haskell:将整数推送到堆栈实现时的无限列表
我试图实现一个简单的堆栈,但我不明白为什么在将整数推入堆栈时会得到一个无限列表 所有其他函数都按照我的预期工作,但我不理解Haskell:将整数推送到堆栈实现时的无限列表,haskell,stack,Haskell,Stack,我试图实现一个简单的堆栈,但我不明白为什么在将整数推入堆栈时会得到一个无限列表 所有其他函数都按照我的预期工作,但我不理解push的问题。当我尝试将一个空堆栈分配给它自己时出错,该堆栈推送了一个变量,如下所示: λ > a = makeStack λ > push 3 a [3] λ > a [] λ > a = push 3 a λ > a [3,3,3,3,3,3,3,3,3,3^CInterrupted. haskell中(我猜,几乎)没有可变的变量(多亏@
push
的问题。当我尝试将一个空堆栈分配给它自己时出错,该堆栈推送了一个变量,如下所示:
λ > a = makeStack
λ > push 3 a
[3]
λ > a
[]
λ > a = push 3 a
λ > a
[3,3,3,3,3,3,3,3,3,3^CInterrupted.
haskell中(我猜,几乎)没有可变的变量(多亏@amalloy纠正了我的术语)
如果你这样写:
x = f x
它进入一个无限循环:f(f(f…
因此,a
的过去值不存在,其中可以推送3
因此,您必须将push 3A
放入其他值中(或直接放入ghci)
不过,这样的循环有时可能会派上用场。请看:
它可以用来做与您相同的事情:
GHCi> fix (push 3)
[3,3,3,3,^CInterrupted.
然而,无限循环并不总是如此。看看:
factFun rec n = if n == 0 then 1 else n * rec (n - 1)
此函数可能看起来像阶乘,但非终止分支中的递归调用被替换为伪(rec
)。我们希望将此伪函数反复替换为factFun
,以获得阶乘。fix
执行以下操作:
GHCi> fix factFun 4
24
注意:我将在这里重复我的评论:如果读者还不知道
fix
,我想邀请他们进行一次漫长而有趣的旅行。我建议他们从这个开始。Haskell不允许变异。在源文件中,如果您定义变量a
,然后尝试重新分配它,就像您在这里对a=push 3所做的那样一个
,你会得到一个编译错误。你不这样做的唯一原因是你在GHCi中工作,它允许你重新定义变量-这纯粹是一种方便,所以你不必在尝试不同的定义时不断想出新的名称
而且,至关重要的是,a=push 3 a
并没有在前一个值的基础上给a
赋予新的值,就像在命令式语言中那样。相反,它是a
本身的定义
这就是为什么您会得到一个无限列表-您的定义处理如下:
a = push 3 a
= 3:a
= 3:(push 3 a)
= 3:(3:a)
等等。由于Haskell的懒惰,像这样的定义没有问题-当你要求完整的列表时,GHCi会像这里一样,一次只计算一个元素,因此一直打印3,直到你告诉它停止为止
要想得到你想要的,你需要输入
push 3a
,或者如果你需要为它指定一个名称,只需从a
中选择一个不同的名称b=push 3a
,然后是b
,它将按照你的预期运行。这是一个非常有趣的函数!我要看看我是否可以使用它!@JiangShiion.我建议你去看看-它说明了很多。这就是我开始的地方。祝你好运:)。Haskell中有很多变量。它们是不可变的。@amalloy你是对的-对不起我的术语。我编辑了这篇文章。我记得在GHCi(或者任何do
块)中有一个特殊的“重新分配”技巧一个使用自身的变量:a Yes,它将在do
块中工作(如果您对其进行去语法化,左边的“new”a
是lambda的参数,因此会将“old”参数隐藏起来),但必须理解这与正常的“赋值”(或者更确切地说是定义)非常不同在Haskell中。我不确定GHCi是否允许这样做,并且必须尝试一下——我知道GHCi隐式地将所有事情解释为发生在IO
monad中,但如果它允许一行带有“左箭头”,我会感到惊讶我提到这个技巧只是因为我看到其他人推荐它,因为do
中的绑定默认是非递归的(使用do rec
显式递归)。(作者认为let
默认是递归的在Haskell中是不幸的。)我个人认为,一个“左箭头”绑定在GHCi中应该是有效的,这并不奇怪,否则就无法获得IO操作的中间结果,并将其绑定到变量以供进一步使用。
GHCi> fix factFun 4
24
a = push 3 a
= 3:a
= 3:(push 3 a)
= 3:(3:a)