Когда использовать `zipmap`, а когда `map vector`?

#clojure

#clojure

Вопрос:

Я спрашивал об особенностях zipmap construct, чтобы только обнаружить, что я, по-видимому, делал это неправильно. Итак, я узнал о (map vector v u) в процессе. Но до этого случая я использовал zipmap для выполнения (map vector ...) работы. Сработало ли это тогда, потому что результирующая карта была достаточно маленькой, чтобы ее можно было отсортировать?

И к актуальному вопросу: в чем польза zipmap и как / когда ее использовать. И когда использовать (map vector ...) ?

Моя первоначальная проблема требовала первоначального порядка, поэтому сопоставление чего-либо не было бы хорошей идеей. Но в принципе — за исключением порядка результирующих пар — эти два метода эквивалентны, потому что seq ‘общая карта становится последовательностью векторов.

 (for [pair (map vector v (rest v))]
  ( ... )) ;do with (first pair) and (last pair)

(for [pair (zipmap v (rest v))]
 ( ... )) ;do with (first pair) and (last pair)
  

Ответ №1:

Используйте (zipmap …), когда вы хотите напрямую создать hashmap из отдельных последовательностей ключей и значений. Результатом является hashmap:

 (zipmap [:k1 :k2 :k3] [10 20 40])
=> {:k3 40, :k2 20, :k1 10}
  

Используйте (map vector …), когда вы пытаетесь объединить несколько последовательностей. На выходе получается ленивая последовательность векторов:

 (map vector [1 2 3] [4 5 6] [7 8 9])
=> ([1 4 7] [2 5 8] [3 6 9])
  

Несколько дополнительных замечаний для рассмотрения:

  • Zipmap работает только с двумя входными последовательностями (ключи значения), тогда как map vector может работать с любым количеством входных последовательностей. Если ваши входные последовательности не являются парами ключ-значение, то это, вероятно, хороший намек на то, что вам следует использовать map vector, а не zipmap
  • zipmap будет более эффективным и простым, чем создание map vector, а затем последующее создание hashmap из пар ключ / значение — например, (into {} (map vector [:k1 :k2 :k3] [10 20 40])) это довольно сложный способ создания zipmap
  • map vector является ленивым — поэтому он приносит немного дополнительных накладных расходов, но очень полезен в обстоятельствах, когда вам действительно нужна лень (например, при работе с бесконечными последовательностями)
  • Вы можете выполнить (seq (zipmap ….)), чтобы получить последовательность пар ключ-значение, подобную (map vector …), однако имейте в виду, что это может изменить порядок следования пар ключ-значение (поскольку промежуточная хэш-карта неупорядочена)

Комментарии:

1. Другое использование zipmap , которое я использую постоянно, — это создание карты из вектора карт.. Рассмотрим, (zipmap (map :id data) data) где данные представляют собой вектор карт, каждая из которых содержит уникальную :id пару ключ / значение.

Ответ №2:

Методы более или менее эквивалентны. Когда вы используете zipmap, вы получаете карту с парами ключ / значение. Когда вы выполняете итерацию по этой карте, вы получаете векторы [key value]. Однако порядок отображения не определен. С помощью конструкции ‘map’ в вашем первом методе вы создаете список векторов с двумя элементами. Порядок определен.

Zipmap может быть немного менее эффективным в вашем примере. Я бы придерживался ‘map’.

Редактировать: О, и zipmap не ленивый. Итак, еще одна причина не использовать это в вашем примере.

Правка 2: используйте zipmap, когда вам действительно нужна карта, например, для быстрого доступа на основе случайного ключа.

Ответ №3:

Они могут показаться похожими, но на самом деле очень отличаются.

  • zipmap создает карту
  • (map vector ...) создает LazySeq из n-кортежей (векторов размером n)

Это две очень разные структуры данных. Хотя ленивая последовательность из двух кортежей может выглядеть похожей на карту, они ведут себя совсем по-другому.

Допустим, мы сопоставляем две коллекции, coll1 и coll2 . Рассмотрим случай, coll1 содержащий повторяющиеся элементы. Вывод zipmap будет содержать только значение, соответствующее последнему появлению дубликатов ключей в coll1 . Вывод (map vector ...) будет содержать 2 кортежа со всеми значениями дублирующихся ключей.

Простой пример REPL:

 => (zipmap [:k1 :k2 :k3 :k1] [1 2 3 4])
{:k3 3, :k2 2, :k1 4}

=>(map vector [:k1 :k2 :k3 :k1] [1 2 3 4])
([:k1 1] [:k2 2] [:k3 3] [:k1 4])
  

Имея это в виду, тривиально видеть опасность в предположении следующего:

Но в принципе — за исключением порядка результирующих пар — эти два метода эквивалентны, потому что последовательная карта становится последовательностью векторов.

Последующая карта становится последовательностью векторов, но не обязательно той же последовательностью векторов, что и результаты из (map vector ...)

Для полноты картины, здесь отсортированы следующие векторы:

 => (sort (seq (zipmap [:k1 :k2 :k3 :k1] [1 2 3 4])))
([:k1 4] [:k2 2] [:k3 3])

=> (sort (seq (map vector [:k1 :k2 :k3 :k1] [1 2 3 4])))
([:k1 1] [:k1 4] [:k2 2] [:k3 3])
  

Я думаю, что ближе всего мы можем подойти к инструкции, подобной приведенной выше,:

set Результат (zip map coll1 coll2) будет равен set результату (map vector coll1 coll2) , если coll1 он сам set .

Это слишком много определителей для двух операций, которые предположительно очень похожи. Вот почему необходимо проявлять особую осторожность при принятии решения, какой из них использовать. Они очень разные, служат разным целям и не должны использоваться взаимозаменяемо.

Ответ №4:

(zipmap k v) принимает две последовательности и возвращает карту (и не сохраняет порядок элементов)

(map vector s1 s2 …) принимает любое количество последовательностей и возвращает seq

используйте первый, когда вы хотите заархивировать две последовательности в карту.

используйте второе, когда вы хотите применить вектор (или список, или любую другую форму для создания последовательности) к нескольким разделениям.

есть некоторое сходство с опцией «сопоставлять» при печати нескольких копий документа 🙂