这与列表无关。有意地,在 Haskell 中根本不可能更新任何值。
为什么?嗯,这就是语言的设计方式。您可能会问为什么命令式语言允许更新值。我的意思是,你写
x = 3
然后你写
x = 4
wut?那么第一个是谎言还是什么?当然,我们应该明确指出我们指的是两个不同的时间变量。这只是询问错误!当然,仅仅保存一些字符并启用一些也可以通过其他更安全的方式实现的低级优化是不值得的吗?
...对吧,不是吗?
即使在命令式语言中,更新(链接)列表中的值实际上也没有多大意义——无论如何您都需要遍历 O (n) 个参数甚至到达应该改变的那个。创建一个全新的列表平均只需要更改旧列表的两倍,但与命令式更新不同,您无需担心其他东西是否仍然需要旧版本,因为您永远不会干扰它。†
无论如何,链表通常都很慢,所以担心 2× 因子也没有多大意义。但是,GHC 经常完全优化列表,因此它们实际上根本不会在内存中构建,而这是编译器更难保证的事情之一,如果它不得不担心某人在其他地方改变列表。
更新 数组 中的元素,这是另一回事。实际上,更新数组中的值在 Haskell 中也很常见,因为这会带来重要的性能提升。 更新数组类型的值仍然是不可能的,但是可以通过对一元可变数组的引用来更新一个值。这最终看起来与在命令式语言中更新数组的方式非常相似,尽管它通常有点冗长,因为所有数组工具都需要从库中导入,而不是使用内置语法。
import qualified Data.Vector.Mutable as VM
main :: IO ()
main = do
myArray <- VM.generate 37 (const "this element isn't set yet")
print =<< VM.read myArray 0
VM.write myArray 0 "newValue"
print =<< VM.read myArray 0
等效 Python:
def main():
myArray = ["this element isn't set yet"] * 37
print(myArray[0])
myArray[0] = "newValue"
print(myArray[0])
然而,很多时候您并不真正需要更新元素。事实上,我们马上就看到了这里的一个问题:你正在索引到一个数组中。这意味着您需要确保索引确实存在。在命令式语言中,这很常见,以至于您几乎不会去想它,但在 Haskell 中,我们真的更喜欢 total 代码,即即使我们不小心交换了两个变量(如两个不同的循环索引),也不应该给出运行时错误,但最好是编译器错误。
通常情况下,如果您更新单个元素,那么您也会更新其他元素。事实上,您经常会按顺序更新它们中的所有,然后与简单地从头开始构建一个包含更新的新列表并没有太大的优势。马上价值观。使用这种方法,几乎不会出错,至少在运行时不会出错。
†这里有一个很大的警告:如果其他人仍在使用旧列表,则意味着垃圾收集器无法回收内存。这就是为什么在 Haskell 中很容易出现内存泄漏的原因 - IMO 是该语言的一个最大问题。