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

Clojure функция коалесценции

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])))))

Фиксированный.

4b9b3361

Ответ 1

На основе ответа на nickik и макроса "clojure:

(defmacro coalesce
    ([] nil)
    ([x] x)
    ([x & next]
       `(let [v# ~x]
           (if (not (nil? v#)) v# (coalesce [email protected])))))

Ответ 2

То, что вы хотите, это макрос "или".

Вычисляет exprs по одному, слева направо. Если форма возвращает логическое истинное значение или возвращает это значение, а не оценивать любое из других выражений, иначе оно возвращает значение последнего выражения. (или) возвращает нуль.

http://clojuredocs.org/clojure_core/clojure.core/or

Если вам нужна только нуль, а не ложь, перепишите ее и назовите ее.

Edit:

Это не может быть сделано как функция, потому что функции сначала оценивают все свои аргументы. Это можно сделать в Haskell, потому что функции ленивы (не на 100% уверены в вещи Haskell).

Ответ 3

Вы можете использовать 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))

Ответ 4

Некоторые версии функций 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 или другое подходящее решение для макросов, если вы хотите получить короткую оценку.

Ответ 5

Возможно, я неправильно понял вопрос, но разве это не первый фильтрованный элемент?

например:.

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