13

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

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

它将被称为:(coalesce f1 f2 f3 ...)where是仅在需要时才应评估fi的表单。如果是非零,那么不应该被评估——它可能有副作用。f1f2

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

编辑:这是我想出的一个解决方案(从 Stuart Halloway 的 Programming 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)))))

固定的。

4

5 回答 5

25

你想要的是“或”宏。

从左到右,一次评估一个 expr。如果一个表单返回一个逻辑真值,或者返回该值并且不计算任何其他表达式,否则它返回最后一个表达式的值。(或)返回零。

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

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

编辑:

这不能作为一个函数来完成,因为函数首先评估它们的所有参数。这可以在 Haskell 中完成,因为函数是惰性的(不是 100% 确定 Haskell 的事情)。

于 2010-11-03T12:22:30.583 回答
4

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

(defmacro coalesce
    ([] nil)
    ([x] x)
    ([x & next]
       `(let [v# ~x]
           (if (not (nil? v#)) v# (coalesce ~@next)))))
于 2010-11-03T12:50:39.833 回答
3

您可以使用 1.2 中引入的 keep:

编辑:扩展答案一点。直接调用的宏。例如的助手。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))
于 2010-11-03T12:48:16.980 回答
2

如果您宁愿避免使用宏,则合并的某些功能版本:

(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如果要进行短路评估,请使用或其他适当的宏解决方案。

于 2015-12-03T16:27:54.457 回答
0

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

例如:

user=> (first (filter (complement nil?) [nil false :foo]))
错误的
user=> (first (filter (complement nil?) [nil :foo]))
:foo
user=> (first (filter (complement nil?) []))
零
user=> (first (filter (complement nil?) nil))
零

它可以缩短为:

(定义合并 [& vals]
  (first (filter (complement nil?) vals)))
用户=>(合并零假:foo)
错误的
用户=>(合并零:foo)
:foo
用户=>(合并为零)
零
用户=>(合并)
零
于 2010-11-04T05:38:32.013 回答