Ответ 1
На основе ответа на nickik и макроса "clojure:
(defmacro coalesce
([] nil)
([x] x)
([x & next]
`(let [v# ~x]
(if (not (nil? v#)) v# (coalesce [email protected])))))
SQL предлагает функцию с именем coalesce(a, b, c, ...)
, которая возвращает значение null, если все его аргументы равны нулю, в противном случае он возвращает первый непустой аргумент.
Как бы вы писали что-то вроде этого в Clojure?
Он будет вызываться следующим образом: (coalesce f1 f2 f3 ...)
, где fi
являются формами , которые должны оцениваться только при необходимости. Если f1
не равно nil, то f2
не следует оценивать - это может иметь побочные эффекты.
Возможно, Clojure уже предлагает такую функцию (или макрос).
РЕДАКТИРОВАТЬ: вот решение, с которым я столкнулся (изменено с помощью программирования Stuart Halloway Programming Clojure, (and ...)
macro на стр. 206):
(defmacro coalesce
([] nil)
([x] x)
([x & rest] `(let [c# ~x] (if c# c# (coalesce [email protected])))))
Кажется, работает.
(defmacro coalesce
([] nil)
([x] x)
([x & rest] `(let [c# ~x] (if (not (nil? c#)) c# (coalesce [email protected])))))
Фиксированный.
На основе ответа на nickik и макроса "clojure:
(defmacro coalesce
([] nil)
([x] x)
([x & next]
`(let [v# ~x]
(if (not (nil? v#)) v# (coalesce [email protected])))))
То, что вы хотите, это макрос "или".
Вычисляет exprs по одному, слева направо. Если форма возвращает логическое истинное значение или возвращает это значение, а не оценивать любое из других выражений, иначе оно возвращает значение последнего выражения. (или) возвращает нуль.
http://clojuredocs.org/clojure_core/clojure.core/or
Если вам нужна только нуль, а не ложь, перепишите ее и назовите ее.
Edit:
Это не может быть сделано как функция, потому что функции сначала оценивают все свои аргументы. Это можно сделать в Haskell, потому что функции ленивы (не на 100% уверены в вещи Haskell).
Вы можете использовать keep, введенный в 1.2:
EDIT: расширенный ответ. Макрос для прямых вызовов. Помощник, например. apply + lazy seq, производя значения.
(defn coalesce*
[values]
(first (keep identity values)))
(defmacro coalesce
[& values]
`(coalesce* (lazy-list [email protected])))
Однако для предотвращения оценки значений необходим какой-то домашний стиль.
Гадкий:
(lazy-cat [e1] [e2] [e3])
В коде немного больше, но красивее:
(defn lazy-list*
[& delayed-values]
(when-let [delayed-values (seq delayed-values)]
(reify
clojure.lang.ISeq
(first [this] @(first delayed-values))
(next [this] (lazy-list* (next delayed-values)))
(more [this] (or (next this) ())))))
(defmacro lazy-list
[& values]
`(lazy-list* [email protected](map (fn [v] `(delay ~v)) values))
Некоторые версии функций coalesce, если вы предпочитаете избегать макросов:
(defn coalesce
"Returns first non-nil argument."
[& args]
(first (keep identity args)))
(defn coalesce-with
"Returns first argument which passes f."
[f & args]
(first (filter f args)))
Использование:
=> (coalesce nil "a" "b")
"a"
=> (coalesce-with not-empty nil "" "123")
"123"
В отличие от спецификации, это будет оценивать все аргументы. Используйте or
или другое подходящее решение для макросов, если вы хотите получить короткую оценку.
Возможно, я неправильно понял вопрос, но разве это не первый фильтрованный элемент?
например:.
user=> (first (filter (complement nil?) [nil false :foo])) false user=> (first (filter (complement nil?) [nil :foo])) :foo user=> (first (filter (complement nil?) [])) nil user=> (first (filter (complement nil?) nil)) nil
Он может быть сокращен до:
(defn coalesce [& vals] (first (filter (complement nil?) vals)))
user=> (coalesce nil false :foo) false user=> (coalesce nil :foo) :foo user=> (coalesce nil) nil user=> (coalesce) nil