【问题标题】:Does creating a (non-list) data structure via fromList actually create the list?通过 fromList 创建(非列表)数据结构实际上会创建列表吗?
【发布时间】:2014-11-15 20:49:27
【问题描述】:

基本上我很好奇代码是否如下:

let myCollection = Data.SomeCollection.fromList [1, 2, foo]

实际上是在执行它看起来在运行时的样子,并创建一个链表作为创建SomeCollection 的中间步骤——或者如果这只是为了语法方便,编译器会避开编译代码中的列表?

如果这是一个愚蠢的问题,我深表歉意,但自从学习了一些 Haskell 后,我就一直想找出答案。

【问题讨论】:

  • 您可能想澄清一下您是否要求 Vector 或更一般地要求任何类型。 Vector 是一种特殊情况,因为它可能具有 Vector 特定的重写规则来融合中间列表。
  • @GabrielGonzalez 谢谢,我编辑澄清如果对任何带有fromList 的集合进行特殊处理,我很感兴趣,尽管如果只有 some 消除列表(也许Vector 例如),这也很高兴知道
  • 恐怕你通常不得不假设有一个实际的列表,除非前面提到的专门的Vector-optimisations 或类似的启动。
  • 一个重要的警告,如果列表恰好是 String,它可能不会通过中间列表,因为这种特殊情况通常是捷径(作为高效 OverloadedStrings 的一部分) .现在有了OverloadedLists,未来版本的数据结构很可能能够避免中间列表。
  • 这里要考虑的另一件事是 GHC 本身有一个 list fusion optimization 这样,当一个“好消费者”函数应用于“好生产者”的结果时,运行时创建一个中间名单被淘汰。像[1, 2, foo] 这样的显式列表有资格成为优秀的生产者。一个特定的fromList 函数是否是一个好的消费者取决于它是如何实现的;这里没有一般规则,只有实现细节。

标签: haskell data-structures compile-time


【解决方案1】:

简答:也许,在某种程度上,但不是真的......

更长的答案:

当您说链表时,您是在以命令式的方式思考。 Haskell 既懒惰又实用,这使得这个问题很难回答。

[a,b,c]a:(b:(c:[])) 的简写。如果我们对此进行全面评估(例如尝试将其打印出来),那么最终在内存中的内容看起来和行为很像 C 中的链表,但有更多的大脑。但这通常不会发生在功能设置中的列表上。大多数基于列表的函数的操作方式是对列表的头部做一些事情,然后将列表的尾部发送到其他地方(可能是同一个地方)。这意味着一个列表真的看起来像x:xs,其中xs 只是一些将创建列表其余部分的函数。 (GHC 术语中的 thunk

整个列表并没有像在命令式语言中那样被创建,然后被处理。它通过fromList 函数一次流式传输

至少这是大多数fromList 函数的工作方式。集合的通用 fromList 可能如下所示:

fromList    :: [a] -> Collection a
fromList    =  Data.List.foldl' insert empty

某些集合可以利用一次添加多个元素的优势。这些集合(我不知道,但我知道它们存在)将建立一个更广泛的内存列表。

然而,除了编译器优化之外,通常情况下

fromList [1, 2, foo]

在计算上是等价的(在一个微小的常数因子内):

empty `insert` 1 `insert` 2 `insert` foo

免责声明:我不太了解任何 Haskell 实现的内部原理,无法绝对肯定地说它们如何评估代码中的列表常量,但这与现实相差不远。如果我离基地很远,我会等待GHC 大师的启发。

【讨论】:

  • 啊,你说的很对,我没想到“懒惰”。假设inserts 链不会导致一堆重新分配+复制(如果任何集合使用底层数组,我的意思是——尽管我认为树是更多常见的 Haskell 方法?)
  • “当你说链表时,你是在用命令式的方式思考”是什么意思?
  • 在命令式语言中,链表是一种数据结构,带有指向下一个元素的内存指针。从表面上看,功能语言中的 CONS 单元看起来很相似,但这种单元实际上同时存在于机器内存中的期望是不正确的。尝试将函数列表概念化为可能在 Java 或 C 中实现它们会导致对函数算法的时间和空间使用的错误直觉。
  • @JohnF.Miller 函数式编程不要求惰性求值。严格的 FP 语言中的链表与 Java 或 C 语言中的链表几乎相同。你所说的适用于 Haskell,而不是一般的 FP。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-09-09
  • 1970-01-01
  • 2015-02-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多