15

为什么在 Scala 中不推荐使用 List 的 + 运算符?

http://www.scala-lang.org/docu/files/api/scala/List.html#%2B%28B%29

4

4 回答 4

18

好问题,所以我在 Odersky 等人的书中查找了它。它说以下内容(希望在这里引用它不是侵犯版权;-)):


为什么不附加到列表?

List不提供追加操作,因为追加到列表所需的时间随着列表的大小线性增长,而添加前面的::时间是恒定的。如果您想通过附加元素来构建列表,您的选择是预先添加它们,然后当您完成调用reverse; 或使用 a ListBuffer,一个提供附加操作的可变列表,当你完成调用toList.


据我了解 FP,附加到列表比附加更常见,至少在纯函数式语言中是这样。我只能假设 Scala 的设计者添加+运算符是为了方便 Java 开发人员,他们习惯于附加 using add(),然后重新考虑它。

于 2010-01-23T14:16:46.917 回答
14

它于 2008 年 5 月在修订版 15071中被弃用,并带有以下消息:

弃用所有有问题的 + 方法,并删除那些从未出现在版本中的方法。

我希望这是为了避免 StringAdd#+ 的歧义。下面比较 2.7.6 和 2.8.0 Beta 之间的区别:

Welcome to Scala version 2.7.6.final (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_15).
Type in expressions to have them evaluated.
Type :help for more information.

scala> List(1) + 2
warning: there were deprecation warnings; re-run with -deprecation for details
res0: List[Int] = List(1, 2)

scala> List(1) + "2"
warning: there were deprecation warnings; re-run with -deprecation for details
res1: List[Any] = List(1, 2)

在 2.8 中,该方法已被删除,您将获得:

Welcome to Scala version 2.8.0.Beta1-RC8 (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_15).
Type in expressions to have them evaluated.
Type :help for more information.

scala> List(1) + 2
<console>:5: error: type mismatch;
 found   : Int(2)
 required: String
       List(1) + 2
                 ^

scala> List(1) + "2"
res1: java.lang.String = List(1)2

更新

在 scala-user 列表中,Martin Odersky 指出:

要找出 List#+ 的真正坏处,请考虑您期望以下产生的结果:

List(1, 2, 3) + " 是正确的结果"

?

oxbow_lakes 补充说——我花了一段时间来解开这个神秘的评论,但我认为关键是它会违反+运算符隐含的类型的交换性。也就是说,的类型a + b应该和的类型相同b + a

更新 Martin Odersky 的澄清:

你会期待什么

List(1, 2, 3) + " 是正确的结果"

生产?我希望有一个字符串:“List(1, 2, 3) 是正确的结果”。

使用 2.7 样式的 List.+,您会得到 List[Any], List(1, 2, 3, "is the correct result")。

我将其归类为一个糟糕的惊喜。总而言之,永远不应该对元素类型协变的集合使用 + 方法。集合和映射是不变的,这就是为什么它们可以有一个 + 方法。这一切都相当微妙和凌乱。如果我们不尝试复制 Java 的 + 用于字符串连接,我们会做得更好。但设计 Scala 时的想法是基本上保留 Java 的所有表达式语法,包括 String +。现在改变它为时已晚。

于 2010-01-23T14:14:45.020 回答
12

它不是可交换的,并且在列表中尤其次优。此外,可变集合和不可变集合之间的行为不同。在 Scala 2.8 上,您有以下内容:

element +: sequence   // prepend
sequence :+ element   // append
sequenece ++ sequence // concatenate

可变/不可变的事情还没有完全解决。作为第一步,一堆方法被弃用,但如果没有一个弃用期,它们就不能被更改或彻底删除。

于 2010-01-23T15:01:08.690 回答
3

如果你关注这个线程,你会发现它可能是因为性能问题。

一般应安排List施工,以免附加。Scala 的 List 是一个不可变的单链表,因此附加到它的末尾是一个 O(n) 操作。

ListA ::: ListB是右关联的,并且运行时间与 ListA 的长度成正比 ' a ::: b' 是 /prepend/ 操作,它O(a.length)及时运行

于 2010-01-23T14:24:57.587 回答