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

Into или vec: преобразование последовательности обратно в вектор в Clojure

У меня есть следующий код, который увеличивает первый элемент каждой пары в векторе:

(vec (map (fn [[key value]] [(inc key) value]) [[0 :a] [1 :b]]))

Однако я боюсь, что этот код неэлегантен, так как он сначала создает последовательность с использованием карты, а затем возвращает ее обратно в вектор.

Рассмотрим этот аналог:

(into [] (map (fn [[key value]] [(inc key) value]) [[0 :a] [1 :b]]))

На # clojure @irc.freenode.net мне сказали, что использование вышеприведенного кода плохо, потому что into расширяется в (reduce conj [] (map-indexed ...)), что создает много промежуточных объектов в процессе. Затем мне сказали, что на самом деле into не распространяется на (reduce conj ...) и использует переходные процессы, когда это возможно. Также измеренное прошедшее время показало, что into на самом деле быстрее, чем vec.

Итак, мои вопросы:

  • Каков правильный способ использования map над векторами?
  • Что происходит под ним, когда я использую vec и into с векторами?

Связанные, но не повторяющиеся вопросы:

4b9b3361

Ответ 1

Фактически с Clojure 1.4.0 предпочтительный способ сделать это - использовать mapv, который похож на map, за исключением того, что его возвращаемое значение является вектором. Это, безусловно, самый эффективный подход, без каких-либо ненужных промежуточных распределений.

Clojure 1.5.0 принесет новую библиотеку редукторов, которая обеспечит общий способ map, filter, take, drop и т.д. при создании векторов, которые можно использовать с into []. Вы можете играть с ним в альфа-версии 1.5.0 и в последних помеченных релизах ClojureScript.

Что касается (vec some-seq) и (into [] some-seq), первый в конечном счете делегирует цикл Java, который выливает some-seq в пустой вектор переходных процессов, а второй делает то же самое в очень эффективном коде Clojure. В обоих случаях есть некоторые начальные проверки, чтобы определить, какой подход следует принимать при построении окончательного возвращаемого значения.

vec и into [] существенно различаются для массивов Java малой длины (до 32) - первая будет иметь псевдоним массива (использовать его как хвост только что созданного вектора) и требует, чтобы массив не быть впоследствии изменено, чтобы содержимое вектора не изменилось (см. докштук); последний создает новый вектор с новым хвостом и не заботится о будущих изменениях в массиве.