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

Значение по умолчанию в clojure maps

У меня есть карта clojure. Назовите его opts. Я знаю два способа получить значения с этой карты, чтобы, если нет ключа, получите какое-то значение по умолчанию:

(let [opts {}
      title-1 (or (:title opts) "Default title")
      title-2 (:title opts "Default title")]
  (println title-1 title-2))

Я видел, что некоторые библиотеки (quil, incanter) использовали первый подход с or. Мне кажется, что второй подход более краток и чище. Есть ли преимущества в первом подходе?
Недостатком использования (or (:title opts) "Default value") является то, что мы не можем передавать false и nil в качестве значений больше, в этом случае всегда используется значение по умолчанию.

4b9b3361

Ответ 1

Важнейшим отличием между (or (:key hash) default) и (:key hash default) является тот факт, что первый оценивает default, только если это необходимо. В последнем случае он всегда оценивается. Поэтому вы должны использовать or, если оценка default стоит дорого.

Другое отличие становится очевидным, когда ваш хэш содержит значения, которые являются ложными в булевом контексте. В случае таких значений (or (:key hash) default) будет оцениваться до default вместо false или nil, который вы ожидаете. В отличие от выражения or, (:key hash default) даст правильные результаты. В качестве примечания обратите внимание дважды, прежде чем хранить nil как значения в хеше.

Хорошо, это были важные отличия. Теперь переходим к младшим.

(or (:title opts) "Default title")

расширяется читателем до

;; Redacted for the sake of brevity.
(let* [x (:title opts)]
  (if x
    x
    "Default title"))

Возможно, он менее эффективен, чем просто оценить

(:title opts "Default title")

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

С другой стороны, на первый взгляд (or (:key hash) :default) кажется более понятным для кого-то, кто не привык к идиоме (:key hash :default). Подумайте о программистах, поступающих с других языков. Например, в Ruby типичный подход к обработке несуществующего элемента хэша

val = hash[:key] || :default

Следовательно, первое выражение может быть проще проанализировать людьми, не привыкшими к некоторым идиомам Clojure.

Ответ 2

Это то, для чего используется клавиша :or при разрушении карты.

(defn f [{:keys [title-1 title-2] :as opts
          :or {title-1 "default-1" title-2 "default-2"}}]
  (println opts)
  (println title-1)
  (println title-2))

что дает вам

> (f {})
{}
default-1
default-2
nil

> (f {:title-1 "foo"})
{:title-1 foo}
foo
default-2
nil

> (f {:title-2 "bar"})
{:title-2 bar}
default-1
bar
nil

> (f {:title-1 "foo" :title-2 "bar"})
{:title-1 foo, :title-2 bar}
foo
bar
nil