Идиоматический поиск 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

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