2017-03-01 81 views
1

我在Haskell,在那里我有一条线,确实是这样写的一段代码:如何修改,而不进入无限循环的Haskell列表?

addElement :: [a] -> a -> [a] 
addElement list elem = list ++ [elem] 

我需要(或者至少,我是这样认为的),这样的功能对于增加的目的我正在实现的图形数据结构的顶点列表中的新顶点。现在,我可以这样称呼这个功能

newlist = addElement oldlist elem 

和一切工作正常。但是,如果我写

mylist = addElement mylist elem 

,然后尝试做MYLIST任何呼叫终止后(它),我进入无限循环,如果我理解正确的话,这是由于哈斯克尔懒惰的评价或者类似的东西(mylist被扩大到addElement (addElement ... elem) elem,如果我得到了它吧?)。

这当然是坏我的特定实现,因为我的目的,我现在每次我需要一个元素添加到列表中的时间,使新的​​列表。那么,如何创建一个以我想要的方式工作的元素添加功能?

+0

mylist = addElement mylist elem'这不是一个赋值,它是一个等式。 Haskell没有更新。在上下文中显示您的尝试。你如何建立mylist? –

+0

那么,我基本上先运行mylist = []',然后说'mylist = addElement mylist 3'。第二个调用等同于'mylist = mylist ++ [3]'。如果我然后在ghci中键入'id mylist',例如没有任何反应,我被困在循环中。所以我应该认为在Haskell中不可能有一个'addElement'函数,最终会以'id mylist'导致'[3]'? –

+0

不,请显示您正在尝试构建mylist的整个函数。 –

回答

3

首先mylist = addElement mylist elem是一个等式,它是不是转让。这是计算一次:因为Haskell是一个说明性语言,你不能改变一个变量:一旦你给它一个价值,它总是有一个值。因此

你的公式将导致:

mylist = addElement mylist elem 
     = addElement (addElement mylist elem) elem 
     = addElement (... (addElement mylist elem) ...) elem 

你的想法。

不过,你不需要每次构建一个完整的新名单:你可以简单地使用(h:t)追加到头部

addElement :: [a] -> a -> [a] 
addElement t h = (h:t) 

这将构建一个“新”名单O(1)一个可重用旧列表作为尾。如前所述,元素将被添加到前面。

解决该问题的另一种方法是使用差异列表。下面的列表表示为:

type DiffList a = a -> [a] 

和空列表是:

emptyDiffList :: DiffList a 
emptyDiffList = \x -> x 

在这种情况下,你地面与差异列表:

groundDiffList :: DiffList a -> [a] 
groundDiffList x = x [] 

,你可以添加到列表末尾的元素与:

addElement :: DiffList a -> a -> DiffList a 
addElement l el = \x -> l (el:x) 

不过你总是需要为“新名单”创建一个新的变量:你不能一下子给mylist另一个值(你当然可以使用递归的,但在这种情况下,那些在技术上两种不同变量:调用者mylist,以及调用者mylist)。

+0

谢谢你的回答,我认为你的第一段和最后一段最能回答我的问题。因此,我必须为每个“新列表”引入新变量的事实基本上是我必须在Haskell中生活的一个局限性,并且我(或任何其他人)都无法做到这一点。这足以让我接受。 –

+4

@ A.Sh一个人的局限是另一个人的解放。 –

+2

@ A.Sh:的确,虽然它不是一个真正的限制:你可以在不重新定义变量的情况下编写任何程序。此外,我个人认为这样做有一定的局限性,因为您不必通过流程推理变量值的变化。在我看来,声明性语言往往更容易调试。 –