在 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
从 the documentation 看来,++
更通用,而 :::
是 List
特定的。提供后者是因为它用于其他功能语言吗?
:::
也是前缀运算符,就像所有以 :
开头的方法一样
遗产。 List 最初被定义为功能语言外观:
1 :: 2 :: Nil // a list
list1 ::: list2 // concatenation of two lists
list match {
case head :: tail => "non-empty"
case Nil => "empty"
}
当然,Scala 以一种特别的方式发展了其他集合。当 2.8 发布时,集合进行了重新设计,以实现最大程度的代码重用和一致的 API,因此您可以使用 ++
连接 any 两个集合 - 甚至是迭代器。然而,List 必须保留其原始运算符,除了一两个已弃用的运算符。
始终使用 :::
。有两个原因:效率和类型安全。
效率
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)
++
也很容易与 +
混淆:
scala> List(1, 2, 3) + "ab"
res1: String = List(1, 2, 3)ab
x ::: y ::: z
替换为 List(x, y, z).flatten
。 pastebin.com/gkx7Hpad
x
和 y
的长度成线性关系(z
在任何情况下都不会迭代,因此对运行时间没有影响,这就是为什么最好将长列表附加到短列表的原因一个,而不是相反),但渐近复杂性并不能说明整个故事。 x ::: (y ::: z)
迭代 y
并附加 z
,然后迭代 x
并附加 y ::: z
的结果。 x
和 y
都迭代一次。 (x ::: y) ::: z
迭代 x
并附加 y
,然后迭代 x ::: y
的结果并附加 z
。 y
仍被迭代一次,但在这种情况下 x
被迭代两次。
:::
仅适用于列表,而 ++
可用于任何可遍历对象。在当前实现 (2.9.0) 中,如果参数也是 List
,则 ++
回退到 :::
。
不同的一点是,第一句被解析为:
scala> List(1,2,3).++(List(4,5))
res0: List[Int] = List(1, 2, 3, 4, 5)
而第二个示例被解析为:
scala> List(4,5).:::(List(1,2,3))
res1: List[Int] = List(1, 2, 3, 4, 5)
所以如果你使用宏,你应该小心。
此外,两个列表的 ++
调用 :::
但开销更大,因为它要求隐式值具有从 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
正如 Daniel C. Sobrai 所说,您可以使用 ++
将任何集合的内容附加到列表中,而使用 :::
您只能连接列表。
:::
而使用++
吗?还要使用+:
而不是::
?::
很有用(参见 Daniel 的第二个示例)。你不能用+:
做到这一点::
和:::
)和其他集合共有的更通用操作是一件好事。我不会从语言中删除任何一个操作。:+
和+:
对象提取器。+:
。没有理由在模式匹配或其他地方使用::
,除非您有审美偏好。