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

Почему в clojure так много функций построения карты?

Начинающий вопрос, но я действительно не понимаю, почему так много операций для построения карт в clojure.

У вас есть conj, assoc и merge, но они, похоже, более или менее выполняют одно и то же?

(assoc {:a 1 :b 2} :c 3)
(conj {:a 1 :b 2} {:c 3})
(merge {:a 1 :b 2} {:c 3})

Какая разница и почему все эти методы необходимы, когда они делают более или менее одно и то же?

4b9b3361

Ответ 1

assoc и conj ведут себя по-разному для других структур данных:

user=> (assoc [1 2 3 4] 1 5)
[1 5 3 4]
user=> (conj [1 2 3 4] 1 5)
[1 2 3 4 1 5]

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

Рассматривайте merge как функцию только для карт (ее аналогично conj для других коллекций).

Мое мнение:

  • assoc - использовать, когда вы меняете существующие пары ключ/значение.
  • conj - используйте, когда вы добавляете новые пары ключ/значение.
  • merge - используйте, когда вы комбинируете две или более карты

Ответ 2

На самом деле эти функции ведут себя по-разному при использовании с картами.

  • conj:

    Во-первых, пример (conj {:a 1 :b 2} :c 3) из текста вопроса не работает вообще (ни с 1.1, ни с 1.2; IllegalArgumentException не выбрасывается). Существует только несколько типов, которые могут быть conj ed на карты, а именно двухэлементные векторы, clojure.lang.MapEntry (которые в основном эквивалентны двухэлементным векторам) и отображениям.

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

    (into a-map (filter a-predicate another-map))
    

    (обратите внимание, что into использует conj - или conj!, когда это возможно - внутренне). Ни merge, ни assoc не позволяют вам сделать это.

  • merge:

    Это почти точно эквивалентно conj, но заменяет его аргументы nil на {} - пустые хэш-карты - и, таким образом, вернет карту, когда первая "карта" в цепочке окажется nil.

    (apply conj [nil {:a 1} {:b 2}])
    ; => ({:b 2} {:a 1}) ; clojure.lang.PersistentList
    (apply merge [nil {:a 1} {:b 2}])
    ; => {:a 1 :b 2} ; clojure.lang.PersistentArrayMap
    

    Отметьте там ничего (кроме docstring...), чтобы остановить программиста от использования merge с другими типами коллекций. Если кто-то это делает, возникает странность; не рекомендуется.

  • assoc:

    Опять же, пример из текста вопроса - (assoc {:a 1 :b 2} {:c 3}) - не будет работать; вместо этого он выкинет IllegalArgumentException. assoc принимает аргумент карты, за которым следует четное число аргументов - в нечетных положениях (допустим, карта находится в позиции 0) являются ключами, а в четных - значениями. Я нахожу, что я assoc вещи на карты чаще, чем я conj, хотя при я conj, assoc будет казаться громоздким.; -)

  • merge-with:

    Для полноты, это последняя базовая функция, касающаяся карт. Мне это очень полезно. Он работает, как указывает указатель. вот пример:

    (merge-with + {:a 1} {:a 3} {:a 5})
    ; => {:a 9}
    

    Обратите внимание, что если в карте содержится "новый" ключ, который не произошел ни на одной из карт слева от него, функция слияния не будет вызываться. Это иногда разочаровывает, но в 1.2 умный reify может предоставить карту с не-t215 > "значениями по умолчанию".

Ответ 3

Так как карты - такая вездесущая структура данных в Clojure, имеет смысл иметь несколько инструментов для их манипулирования. Различные различные функции все синтаксически удобны в несколько разных обстоятельствах.

Мое личное внимание к конкретным функциям, которые вы упомянули:

  • Я использую assoc, чтобы добавить одно значение к карте с учетом ключа и значения
  • Я использую merge для объединения двух карт или добавления нескольких новых записей сразу
  • Обычно я не использую conj с картами, поскольку я мысленно сопоставляю их с списками