【问题标题】:Why is the + operator for List deprecated in Scala?为什么在 Scala 中不推荐使用 List 的 + 运算符?
【发布时间】:2010-01-23 13:34:57
【问题描述】:

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

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

【问题讨论】:

    标签: list scala operator-overloading deprecated


    【解决方案1】:

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


    为什么不追加到列表中?

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


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

    【讨论】:

    • 实际上,这甚至不是真正的 FP 事情,它是持久递归链表如何工作的一般特征:前置是更新单个指针/引用/您想要调用它的任何内容,追加需要复制整个列表。因此,前置是 O(1) 和一个很小的常数因子,附加是 O(n) 和一个相当大的 c
    • 嗯,O(n) 的复杂性是由于需要复制列表,而这是由于需要具有不可变列表。仅使用不可变数据是函数式编程语言的标志。所以我仍然会说这是一个FP的事情。在 Java 的列表类型中,或者例如C++ 中的std::list::push_back 附加有O(1)
    【解决方案2】:

    它已于 2008 年 5 月在 revision 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 用户列表中,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.+,您会得到 a List[Any], List(1, 2, 3, "是 正确的结果”)。

    我将其归类为一个糟糕的惊喜。在 总结,一个人永远不应该有 + 对元素类型协变的集合的方法。套 而且地图是不变的,这就是为什么 他们可以有一个 + 方法。就是这样 相当精致和凌乱。我们会 如果我们不尝试的话会更好 重复 Java 的 + for String 级联。但是当 Scala 得到 设计的想法是保持 基本上所有的 Java 表达式 语法,包括字符串 +。它是 现在改变它为时已晚。

    【讨论】:

    • 虽然这种事情是自动类型转换的普遍问题。我不明白,为什么 List.+ 运算符在这里是坏人。实际上唯一让我感到困惑的结果是最后一个。
    • Predef.anyToStringEquals 是对 Java 兼容性的一种不幸的尊重。我同意这令人困惑。但是,查看其他帖子,弃用似乎与此没有直接关系。
    【解决方案3】:

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

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

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

    【讨论】:

    • 这些运算符都不是可交换的。事实上,集合运算符很少是这样的。
    • 是的,但是 +::+ 并不是人们期望可交换的众所周知的运算符。
    • 这就是我要找的!作为一名 java 开发人员和 scala 初学者,所有花哨的运算符都让我发疯了......
    【解决方案4】:

    如果你关注this thread,你会发现它可能是因为性能问题。

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-09-08
      • 2021-03-17
      • 1970-01-01
      • 1970-01-01
      • 2011-09-19
      • 2016-07-07
      • 2016-02-23
      • 2017-11-04
      相关资源
      最近更新 更多