Конкатенация списка Scala, ::: vs ++ - PullRequest
335 голосов
/ 03 июля 2011

Есть ли разница между ::: и ++ для объединения списков в 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

Из документации похоже, что ++ является более общим, тогда как ::: является List -специфичным.Предоставляется ли последний, потому что он используется в других функциональных языках?

Ответы [ 4 ]

299 голосов
/ 03 июля 2011

Наследие.Первоначально список был определен как выглядящий на функциональных языках:

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, так что вы можете использовать ++ для объединения любых двух коллекций - и даже итераторов.Список, однако, должен сохранить свои оригинальные операторы, кроме одного или двух, которые устарели.

81 голосов
/ 03 июля 2011

::: работает только со списками, в то время как ++ может использоваться с любым перемещаемым.В текущей реализации (2.9.0) ++ возвращается к :::, если аргумент также равен List.

75 голосов
/ 21 августа 2015

Всегда используйте :::.Есть две причины: эффективность и безопасность типов.

Эффективность

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
20 голосов
/ 20 февраля 2014

Другое дело, что первое предложение анализируется как:

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)

Так что, если вы используете макросы, вы должнызаботиться.

Кроме того, ++ для двух списков вызывает :::, но с дополнительными затратами, потому что он запрашивает неявное значение, чтобы иметь построитель из списка в список.Но микробенчмарки не доказали ничего полезного в этом смысле, я думаю, что компилятор оптимизирует такие вызовы.

Микро-тесты после прогрева.

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

Как сказал Даниэль С. Собрай, выможно добавлять содержимое любой коллекции в список с помощью ++, тогда как с ::: вы можете только объединять списки.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...