在lisp中的副作用与非破坏性

时间:2014-04-14 12:18:55

标签: haskell lisp

例如,在Mathematica程序中为列表上执行的每个操作生成新列表的速度比列表中的位置操作(比如追加,删除等)快得多。

Haskell默认情况下为列表上执行的每个操作提供一个新列表。

我读到lisp提供了两种设施,每次创建新列表或选择在列表中进行就地操作。

但我感到困惑,因为它还说明在适当的位置有助于重新使用相同的列表,从而减少垃圾。那么,哪种方式是有效的,因为我看到两种语言相反的东西。

3 个答案:

答案 0 :(得分:3)

不可变数据结构让编译器reason about them easier。 (有些狂热者甚至认为可变数据结构对于糟糕的编译器来说是一个拐杖。: - )

可变结构(数组,列表,哈希表 - 所有这些都存在于Lisp中)让人们更容易做事,而且他们有时会让程序员对代码进行微观优化,这在&#时并不是那么愚蠢34; sufficiently smart compiler"甚至比现在更像是一个梦想。

PS。将Haskell和Mathematica与Lisp进行比较是不公平的,因为它们属于不同的世代。 Lisp是仍在使用的第二个最古老的编程语言(在Fortran之后),而Haskell和Mathematica则建立在30多年的CS研究之上。

答案 1 :(得分:2)

  

但是我感到很困惑,因为它也说明在适当的地方有助于重复使用   同样的清单,因此减少垃圾。那么,哪种方式是   有效,因为我看到两种很棒的语言   对面。

Haskell的主要设计目标之一是简单的推理。当您的所有数据结构都是不可变的时,您将获得参照透明度。这使您的代码易于推理。这样做的其他优点是易于并发和并行。

在Mathematica中,他们似乎建议使用不可变数据表,但也通过仿真提供可变数据结构。虽然,我认为这不是一个很好的设计决定,但根据它们提供了灵活性。

在Haskell中,列表不是数据结构的开始和结束。还有更多:Set,Vector,Map,Array等。每个数据结构都有自己的特性。为了获得良好的性能,我建议您了解每个的优缺点,并根据您的要求使用正确的数据结构。对于大多数简单的要求,List的使用就足够了。

如果出于某种原因,你真的想要Haskell中的可变数据对象,你可能想要查看ST monad。

答案 2 :(得分:1)

正如其他人所指出的,不可变数据结构使代码易于推理。这不仅仅是程序员:编译器也可以更容易地推断代码。

例如,考虑以下身份:

map (f . g) xs = (map f . map g) xs

如果你不了解Haskell,"映射f xs"依次将函数f应用于列表X的每个元素,为您提供一个新列表,以及"。"是一个将两个函数组合在一起的运算符;定义是"(f.g)x = f(g x)"。

上面的标识不是" map"的定义的一部分,但是你可以通过对地图的定义和"来证明它。"。"作为公理并做一些替代。它说的是你可以替换" map f。地图g" (即,列表中的两次迭代),使用" map(f.g)" (也就是说,单个迭代执行" g"然后" f"依次对每个项目执行)。但这只有在你认为" f"和" g"没有副作用。如果他们的执行顺序很重要,那么身份就不再成立。所以你只能在所有值都是不可变的语言中进行优化。

GHC允许程序员使用rewrite rules声明这些身份。 Data.List中的实际规则集更为复杂和通用,但上面的规则是一个特殊情况,遵循它给出的规则。

因此,在Haskell中,您可以编写一长串的列表操作,如

foo = take 5 . sortBy cmp . map f . filter pred . concatMap g

并将其编译成最佳代码。值可变的语言必须在整个列表中执行每个步骤,将结果存储在内存中,然后继续下一步。对于速度太慢和/或内存耗尽的大型列表,因此必须手动重写上述表达式,而不是让编译器为您执行此操作。