Подтвердить что ты не робот

Какая разница между `::` и `+:` для добавления в список?

List имеет 2 метода, которые указаны для добавления элемента в список (неизменяемый):

  • +: (реализация Seq.+:) и
  • :: (определен только в List)

+: технически имеет более общую подпись типа -

def +:[B >: A, That](elem: B)(implicit bf: CanBuildFrom[List[A], B, That]): That
def ::[B >: A](x: B): List[B]

но игнорируя неявный, который согласно сообщению doc просто требует, чтобы That был List[B], подписи эквивалентны.

В чем разница между List.+: и List.::? Если они на самом деле идентичны, я полагаю, что +: было бы предпочтительнее избегать зависимости от конкретной реализации List. Но почему был определен другой публичный метод, и когда клиентский код назовет его?

Изменить

Существует также экстрактор для :: в сопоставлении с образцом, но мне интересно об этих конкретных методах.

См. также: Scala конкатенация списка,: vs ++

4b9b3361

Ответ 1

Лучший способ определить разницу между обоими методами - посмотреть исходный код.

источник :::

def ::[B >: A] (x: B): List[B] =
  new scala.collection.immutable.::(x, this)

источник +::

override def +:[B >: A, That](elem: B)(implicit bf: CanBuildFrom[List[A], B, That]): That = bf match {
  case _: List.GenericCanBuildFrom[_] => (elem :: this).asInstanceOf[That]
  case _ => super.+:(elem)(bf)
}

Как вы можете видеть, для List оба метода делают одно и то же (компилятор выберет List.canBuildFrom для CanBuildFrom).

Итак, какой метод использовать? Обычно можно выбрать интерфейс (+:), чем реализация (::), но поскольку List является общей структурой данных на функциональных языках, у нее есть свои собственные методы, которые широко используются. Многие алгоритмы создают способ работы List. Например, вы найдете множество методов, которые добавляют отдельные элементы к List или вызывают удобные методы head или tail, потому что все эти операции O(1). Поэтому, если вы работаете локально с List (внутри отдельных методов или классов), нет никаких проблем с выбором List -специфических методов. Но если вы хотите общаться между классами, т.е. Хотите написать некоторые интерфейсы, вы должны выбрать более общий интерфейс Seq.

Ответ 2

+: является более общим, так как он позволяет типу результата отличаться от типа объекта, на который он вызван. Например:

scala> Range(1,4).+:(0)
res7: scala.collection.immutable.IndexedSeq[Int] = Vector(0, 1, 2, 3)