我正在Haskell中编写一段代码,我有一行代码如下:
addElement :: [a] -> a -> [a]
addElement list elem = list ++ [elem]
我需要(或者至少我认为)这样的函数,以便在我实现的图形数据结构的顶点列表中添加新顶点。现在,我可以按如下方式调用此函数
newlist = addElement oldlist elem
一切都很好。但是,如果我写
mylist = addElement mylist elem
然后在调用终止后尝试用mylist做任何事情(确实如此),我进入一个无限循环,如果我理解正确,这是由于对Haskell的懒惰评估或类似的事情({{1如果我做对了,我会扩展到mylist
。
这对我的特定实现当然不好,因为为了我的目的,我现在每次需要向列表添加元素时都必须创建新列表。那么如何使元素添加功能按照我想要的方式工作呢?
答案 0 :(得分: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
。