Идиоматический поиск clojure по ключевому слову
Скажем, у меня есть карта clojure, которая использует ключевые слова в качестве своих ключей:
(def my-car {:color "candy-apple red" :horsepower 450})
Я знаю, что я могу найти значение, связанное с ключевым словом, либо используя ключевое слово или карту как функцию, а другое как свой аргумент:
(my-car :color)
; => "candy-apple red"
(:color my-car)
; => "candy-apple red"
Я понимаю, что обе формы могут пригодиться в определенных ситуациях, но один из них считается более идиоматичным для простого использования, как показано выше?
Ответы
Ответ 1
(:color my-car)
является довольно стандартным. Для этого есть несколько причин, и я не буду вдаваться в них. Но вот пример.
Поскольку :color
является константой, а my-car
не является, hotspot может полностью встроить динамическую отправку color.invoke(m)
, которую она не может сделать с m.invoke(color)
(в некотором java-псевдокоде).
Это становится еще лучше, если my-car
иногда бывает записью с полем color
вместо простой карты: компилятор clojure может испускать код для проверки "эй, если my-car
является экземпляром CarType, то просто верните my-car.color
, в противном случае выполните весь сложный, медленный поиск в хэшмапе."
Ответ 2
Из стандартов кодирования библиотеки:
-
Используйте синтаксис ключевого слова для доступа к свойствам объектов:
(:property object-like-map)
-
Использовать синтаксис первой строки для извлечения значений из коллекции (или использовать get, если коллекция может быть нулевой).
(collection-like-map key)
(get collection-like-map key)
Ответ 3
Я собрал список аргументов для двух форм и против них. ( Изменить: Добавлен третий вариант - (get map :key)
, который является моим новым фаворитом, несмотря на то, что он немного более подробный)
Аргументы для (: map map)
1) Запрошено в стандартах кодирования
http://dev.clojure.org/display/community/Library+Coding+Standards
2) Все еще работает, когда карта равна nil
> (:a nil)
nil
> (nil :a)
ERROR: can't call nil
--- контраргумент --- если ключ может быть нулем, другие формы лучше
> ({:a "b"} nil)
nil
> (nil {:a "b"})
ERROR: can't call nil
3) Лучше работает для потоковой передачи и сопоставления с коллекциями объектов
(-> my-map
:alpha
fn-on-alpha
:beta
fn-on-beta
:gamma
> (def map-collection '({:key "values"} {:key "in"} {:key "collection"}))
> (map :key map-collection)
("values" "in" "collection")
--- counterargument --- структура кода потока различается, чем
обычные обычные идиоматические тенденции могут применяться для доступа к карте
при необходимости
4) Потенциальная оптимизация? (требуется проверка)
Аргументы для (map: key)
1) Не бросает ошибку, когда ключ не является ключевым словом или nil
> ({:a "b"} nil)
nil
> (nil {:a "b"})
ERROR: can't call nil
> ({"a" "b"} "a")
"b"
> ("a" {"a" "b"})
ERROR: string cannot be cast to IFn
2) Согласованность с доступом к списку в Clojure
> ([:a :b :c] 1)
:b
> (1 [:a :b :c])
ERROR: long cannot be cast to IFn
3) Сходство с другими формами доступа к объектам
java> my_obj .alpha .beta .gamma .delta
clj > ((((my-map :alpha) :beta) :gamma) :delta)
clj > (get-in my-map [:alpha :beta :gamma :delta])
cljs> (aget js-obj "alpha" "beta" "gamma" "delta")
4) Выравнивание при обращении к нескольким клавишам с одной и той же карты (отдельные строки)
> (my-func
(my-map :un)
(my-map :deux)
(my-map :trois)
(my-map :quatre)
(my-map :cinq))
> (my-func
(:un my-map)
(:deux my-map)
(:trois my-map)
(:quatre my-map)
(:cinq my-map))
--- контраргумент --- выравнивание хуже при доступе к одному ключу с нескольких карт
> (my-func
(:key map-un)
(:key map-deux)
(:key map-trois)
(:key map-quatre)
(:key map-cinq)
> (my-func
(map-un :key)
(map-deux :key)
(map-trois :key)
(map-quatre :key)
(map-cinq :key)
Аргументы для (получить карту: ключ)
1) НИКОГДА не вызывает ошибку, если arg1 - map/vector/nil, а arg2 - key/index/nil
> (get nil :a)
nil
> (get nil nil)
nil
> (get {:a "b"} nil)
nil
> (get {:a "b"} :q)
nil
> (get [:a :b :c] nil)
nil
> (get [:a :b :c] 5)
nil
2) Согласованность в форме с другими Clojure функциями
> (get {:a "b"} :a)
:b
> (contains? {:a "b"} :a)
true
> (nth [:a :b :c] 1)
:b
> (conj [:a :b] :c)
[:a :b :c]
3) Преимущества выравнивания карты-первых
> (my-func
(get my-map :un)
(get my-map :deux)
(get my-map :trois)
(get my-map :quatre)
(get my-map :cinq))
4) Get-in может использоваться для вложенного доступа с помощью одного вызова
> (get-in my-map [:alpha :beta :gamma :delta])
> (aget js-obj "alpha" "beta" "gamma" "delta")
Источник: тестирование http://tryclj.com/
Ответ 4
Я бы сказал, что это либо идиоматично. Единственное предостережение в том, что вторая форма работает только с ключевыми словами. Который, я предполагаю, будучи преднамеренным выбором дизайна, дал бы ему больше оснований быть идиоматичным.