Почему добавление к списку плохое?

Недавно я начал изучать scala, и я столкнулся с функцией :: (cons), которая добавляется к списку.
В книге "Программирование в Scala" указано, что нет функции добавления, поскольку добавление к списку имеет производительность o (n), тогда как прединдж имеет производительность o (1)

Что-то просто поражает меня как неправильное в этом утверждении.

Не зависит ли производительность от реализации? Невозможно ли просто реализовать список с прямыми и обратными ссылками и сохранить первый и последний элементы в контейнере?

Второй вопрос, который я предполагаю, - это то, что я должен делать, когда у меня есть список, скажем 1,2,3, и я хочу добавить 4 до конца?

Ответы

Ответ 1

Ключ в том, что x :: somelist не мутирует somelist, а создает новый список, содержащий x, за которым следуют все элементы somelist. Это можно сделать в O (1) раз, потому что вам нужно установить somelist как преемника x во вновь созданный, отдельно связанный список.

Если вместо этого использовались дважды связанные списки, x также должен был быть установлен как предшественник главы somelist, который изменил бы somelist. Поэтому, если мы хотим иметь возможность делать :: в O (1) без изменения исходного списка, мы можем использовать только отдельные списки.

Что касается второго вопроса: вы можете использовать ::: для объединения одного элемента списка в конец вашего списка. Это операция O (n).

List(1,2,3) ::: List(4)

Ответ 2

Другие ответы дали хорошие объяснения этому явлению. Если вы добавляете множество элементов в список в подпрограмме или если вы создаете список, добавляя элементы, функциональная идиома состоит в том, чтобы создать список в обратном порядке, минуя элементы в начале списка, затем переверните его в конце. Это дает вам O (n) производительность вместо O (n²).

Ответ 3

Prepending работает быстрее, потому что требуется только две операции:

  • Создайте новый список node
  • Укажите, что новый node указывает на существующий список

При добавлении требуется больше операций, потому что вам нужно пройти в конец списка, поскольку у вас есть только указатель на голову.

Я никогда не программировал в Scala раньше, но вы можете попробовать List Buffer

Ответ 4

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

В сегодняшнем Scala вы можете просто использовать xs :+ x для добавления элемента в конце любой последовательной коллекции. (Существует также x +: xs для добавления. Мнемоника для многих операций по сборке Scala 2.8+ состоит в том, что col on идет рядом с col lection.)

Это будет O (n) со связанной по умолчанию реализацией List или Seq, но если вы используете Vector или IndexedSeq, это будет эффективно постоянное время. Scala Vector, вероятно, Scala самая полезная коллекция в виде списка, в отличие от Java Vector, которая в наши дни бесполезна.

Если вы работаете в Scala 2.8 или более поздней версии, введение коллекций является абсолютной необходимостью читать.

Ответ 5

В большинстве функциональных языков заметно выделяется структура данных с односвязным списком, так как это удобный тип неизменной коллекции. Когда вы говорите "список" на функциональном языке, это обычно означает то, что вы имеете в виду (односвязный список, обычно неизменный). Для такого типа append - O (n), тогда как cons - O (1).