Scala list concatenation,::: vs ++

Lui*_*hys 345 scala list concatenation

在Scala中连接列表:::++列表之间是否有任何区别?

scala> List(1,2,3) ++ List(4,5)
res0: List[Int] = List(1, 2, 3, 4, 5)

scala> List(1,2,3) ::: List(4,5)
res1: List[Int] = List(1, 2, 3, 4, 5)

scala> res0 == res1
res2: Boolean = true
Run Code Online (Sandbox Code Playgroud)

文档看起来++是更普遍的,而:::List特异性的.提供后者是因为它用于其他功能语言吗?

Dan*_*ral 304

遗产.列表最初定义为功能语言:

1 :: 2 :: Nil // a list
list1 ::: list2  // concatenation of two lists

list match {
  case head :: tail => "non-empty"
  case Nil          => "empty"
}
Run Code Online (Sandbox Code Playgroud)

当然,Scala以临时方式演变了其他集合.当2.8出来时,这些集合经过重新设计,以实现最大程度的代码重用和一致的API,因此您可以使用它++来连接任何两个集合 - 甚至是迭代器.然而,列表必须保留其原始运营商,除了一两个被弃用的运营商.

  • 由于模式匹配,`::`很有用(参见Daniel第二个例子).你不能用`+:`来做到这一点 (36认同)
  • @paradigmatic Sc​​ala 2.10有`:+`和`+:`对象提取器. (21认同)
  • 那么最好的做法是避免使用`:::`来支持`++`吗?还使用`+:`而不是`::`? (18认同)
  • 我觉得很好的是,它既有List惯用操作(比如`::`和`:::`),也有更常见的操作,这些操作对于其他集合来说很常见.我不会放弃语言中的任何操作. (2认同)
  • @samthebest 您可以在模式匹配中使用 `+:`。没有理由在模式匹配或其他地方使用 `::`,除非是审美偏好,如果你有的话。 (2认同)

par*_*tic 83

:::仅适用于列表,同时++可以与任何可遍历使用.在当前的实现(2.9.0),++倒在:::如果参数也是List.

  • 所以使用:::和++同时使用list非常容易.这可能会给代码/风格带来麻烦. (4认同)

Zhe*_*lov 81

一直用:::.有两个原因:效率和类型安全.

效率

x ::: y ::: z是快于x ++ y ++ z,因为:::是正确的联想.x ::: y ::: z被解析为x ::: (y ::: z),在算法上比(x ::: y) ::: z(后者需要O(| x |)更多的步骤更快).

类型安全

随着:::你只能连接两个List秒.随++你可以附加任何集合List,这是可怕的:

scala> List(1, 2, 3) ++ "ab"
res0: List[AnyVal] = List(1, 2, 3, a, b)
Run Code Online (Sandbox Code Playgroud)

++也容易混淆+:

scala> List(1, 2, 3) + "ab"
res1: String = List(1, 2, 3)ab
Run Code Online (Sandbox Code Playgroud)

  • 当连接两个列表时,没有区别,但是在3个或更多的情况下,你有一个好点,我用快速基准确认了它.但是,如果你担心效率,`x ::: y ::: z`应该用`List(x,y,z).flatten`代替.http://pastebin.com/gkx7Hpad (8认同)
  • @pacman列表是单独链接的,要将一个列表附加到另一个列表,您需要制作第一个列表的副本,该列表最后附加第二个列表.因此,对于第一列表中的元素数,级联是O(n).第二个列表的长度不会影响运行时,因此最好将长列表附加到短列表而不是将短列表附加到长列表中. (6认同)
  • @pacman的复杂性始终是线性WRT x`的`长度和`y`(`z`是从来没有在任何情况下迭代,从而对运行时没有任何影响,这就是为什么它是更好的一个长长的清单追加到短一,而不是相反)但渐近的复杂性并不能说明整个故事.`x :::(y ::: z)`迭代`y`并追加`z`,然后迭代`x`并追加`y ::: z`的结果.`x`和`y`都迭代一次.`(x ::: y)::: z`迭代`x`并附加`y`,然后迭代`x ::: y`的结果并追加`z`.`y`仍然迭代一次但在这种情况下```迭代两次. (4认同)
  • 请解释一下,为什么左关联连接需要更多的O(x)步骤.我认为它们都适用于O(1). (3认同)

Mik*_*yer 20

另一点是第一句被解析为:

scala> List(1,2,3).++(List(4,5))
res0: List[Int] = List(1, 2, 3, 4, 5)
Run Code Online (Sandbox Code Playgroud)

而第二个例子被解析为:

scala> List(4,5).:::(List(1,2,3))
res1: List[Int] = List(1, 2, 3, 4, 5)
Run Code Online (Sandbox Code Playgroud)

所以如果你使用宏,你应该小心.

此外,++两个列表正在调用:::但具有更多开销,因为它要求一个隐式值以从List到List具有构建器.但微基准测试在这个意义上没有证明任何有用的东西,我想编译器会优化这些调用.

热身后的微观基准.

scala>def time(a: => Unit): Long = { val t = System.currentTimeMillis; a; System.currentTimeMillis - t}
scala>def average(a: () => Long) = (for(i<-1 to 100) yield a()).sum/100

scala>average (() => time { (List[Int]() /: (1 to 1000)) { case (l, e) => l ++ List(e) } })
res1: Long = 46
scala>average (() => time { (List[Int]() /: (1 to 1000)) { case (l, e) => l ::: List(e ) } })
res2: Long = 46
Run Code Online (Sandbox Code Playgroud)

正如Daniel C. Sobrai所说,您可以使用任何集合的内容附加到列表中++,而:::您只能连接列表.

  • 请发布你不过于简单的微基准测试,我会投票给他们. (20认同)