Clojure合并功能

时间:2010-11-03 12:10:41

标签: clojure coalesce

SQL提供了一个名为coalesce(a, b, c, ...)的函数,如果它的所有参数都为null,则返回null,否则返回第一个非null参数。

你会如何在Clojure中写这样的东西?

将按以下方式调用:(coalesce f1 f2 f3 ...)其中fi为表单,只应在需要时进行评估。如果f1为非零,则不应评估f2 - 它可能会产生副作用。

也许Clojure已经提供了这样的功能(或宏)。

编辑:这是我提出的解决方案(修改自Stuart Halloway的编程Clojure,(and ...)宏(第206页):

(defmacro coalesce
  ([] nil)
  ([x] x)
  ([x & rest] `(let [c# ~x] (if c# c# (coalesce ~@rest)))))

似乎工作。

(defmacro coalesce
  ([] nil)
  ([x] x)
  ([x & rest] `(let [c# ~x] (if (not (nil? c#)) c# (coalesce ~@rest)))))

固定。

5 个答案:

答案 0 :(得分:22)

你想要的是“或”宏。

  

从左到右逐个评估exprs。如果是表格   返回逻辑真值,或返回该值而不返回   评估任何其他表达式,否则返回   最后一个表达式的值。 (或)返回nil。

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

如果您只想要nil而不是false,请重写并命名为coalesce。

编辑:

这不能作为函数完成,因为函数首先评估所有参数。这可以在Haskell中完成,因为函数是懒惰的(不是100%肯定Haskell的事情)。

答案 1 :(得分:4)

基于nickik的回答和“或”clojure宏:

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

答案 2 :(得分:3)

您可以使用1.2中引入的保持:

编辑:扩展答案一点点。直接调用的宏。助手,例如。 apply + lazy seq产生值。

(defn coalesce*
  [values]
  (first (keep identity values)))

(defmacro coalesce
  [& values]
  `(coalesce* (lazy-list ~@values)))

然而,为了防止对价值进行评估,人们需要一些本土的方式。

丑:

(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* ~@(map (fn [v] `(delay ~v)) values))

答案 3 :(得分:2)

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"

与规范不同,这将评估所有args。如果您想进行短路评估,请使用or或其他适当的宏解决方案。

答案 4 :(得分:0)

也许我误解了这个问题,但这不仅仅是第一个过滤后的元素吗?

E.g:

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