Идиоматический поиск карт по ключевым словам - PullRequest
29 голосов
/ 12 августа 2011

Скажем, у меня есть карта clojure, в которой в качестве ключей используются ключевые слова:

(def my-car {:color "candy-apple red" :horsepower 450})

Я знаю, что могу посмотреть значение, связанное с ключевым словом, используя ключевое слово или карту в качестве функции, а другое в качестве аргумента:

(my-car :color)
; => "candy-apple red"
(:color my-car)
; => "candy-apple red"

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

Ответы [ 4 ]

39 голосов
/ 12 августа 2011

(:color my-car) довольно стандартно. Для этого есть несколько причин, и я не буду вдаваться во все из них. Но вот пример.

Поскольку :color является константой, а my-car - нет, точка доступа может полностью встроить динамическую диспетчеризацию color.invoke(m), чего не может сделать с m.invoke(color) (в некотором псевдокоде Java).

Это становится еще лучше, если my-car иногда оказывается записью с полем color вместо простой карты: компилятор clojure может выдать код для проверки "эй, если my-car является экземпляром CarType, затем просто верните my-car.color, в противном случае выполните весь сложный, медленный поиск по хеш-карте. "

24 голосов
/ 12 августа 2011

Из библиотеки стандартов кодирования :

  • Использовать синтаксис с первым ключевым словом для доступа к свойствам объектов:

    (:property object-like-map)
    
  • Использовать синтаксис «первая коллекция» для извлечения значений из коллекции (или использовать get, если коллекция может иметь значение «ноль»).

    (collection-like-map key)
    (get collection-like-map key)
    
8 голосов
/ 12 сентября 2014

Я собрал список аргументов за и против двух форм. ( Редактировать: Добавлен третий вариант - (get map :key), который является моим новым фаворитом, хотя и немного более многословен)

Аргументы для (: карта ключей)

1) Запрошено в стандартах кодирования

http://dev.clojure.org/display/community/Library+Coding+Standards

2) По-прежнему работает, когда карта равна нулю

> (: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")

--- контраргумент --- структура кода потоков отличается от обычно так разные идиоматические тенденции могут быть применены для доступа к карте при необходимости

4) Потенциальная выгода от оптимизации? (требуется проверка)

Аргументы для (карта: ключ)

1) Не выдает ошибку, когда ключ не является ключевым словом или ноль

> ({: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 - карта / вектор / ноль, а arg2 - ключ / индекс / ноль

> (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/

0 голосов
/ 12 августа 2011

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...