我只使用了3种函数式语言 - scala,erlang和haskell,但在所有这三种语言中,构建列表的正确方法是将新数据添加到前面,然后将其反转而不是仅仅追加到结束。当然,您可以附加到列表中,但这会导致构建一个全新的列表。
这是为什么?我可以想象这是因为列表在内部实现为链接列表,但为什么它们不能仅作为双链表实现,所以你可以追加到最后没有惩罚?是否有某些原因使所有功能语言都有这种限制?
答案 0 :(得分:21)
函数式语言中的列表是不可变的/持久的。
附加到不可变列表的前面是很便宜的,因为你只需要分配一个节点,下一个指针指向前一个列表的头部。没有必要更改原始列表,因为它只是一个单链表,并且指向前一个头的指针无法看到更新。
将节点添加到列表末尾需要修改最后一个节点以指向新创建的节点。只有这是不可能的,因为节点是不可变的。唯一的选择是创建一个与最后一个节点具有相同值并指向新创建的尾部的新节点。此过程必须一直重复到列表的前面,从而产生一个全新的列表,该列表是除尾节点之外的第一个列表的副本。因此更贵。
答案 1 :(得分:5)
答案 2 :(得分:2)
他们当然可以支持附加功能,但前提是他们限制API的速度要快得多。它也是一种无法附加的功能,因为您必须修改最后一个元素或创建一个全新的列表。 Prepend在本质上以不可变的,功能性的风格工作。
答案 3 :(得分:2)
这就是定义列表的方式。列表已定义作为以nil终止的链接列表,这不仅仅是一个实现细节。再加上这些语言具有不可变数据,至少是erlang和haskell,意味着你不能将它们实现为双链表。添加元素会修改列表,这是非法的。
答案 4 :(得分:1)
通过将列表构造限制为前置,这意味着任何其他人对列表的某些部分的引用进一步向下,将不会在其背后意外地看到它。这允许有效的列表构造,同时保留不可变数据的属性。