Clojure 合并函数

发布于 2024-09-30 14:14:04 字数 737 浏览 11 评论 0原文

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

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

它将像这样调用:(coalesce f1 f2 f3 ...),其中 fi仅在需要时才进行评估的形式。如果f1非零,则不应评估f2——它可能会产生副作用。

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

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

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

固定的。

SQL offers a function called coalesce(a, b, c, ...) that returns null if all of its arguments are null, otherwise it returns the first non-null argument.

How would you go about writing something like this in Clojure?

It will be called like this: (coalesce f1 f2 f3 ...) where the fi are forms that should only be evaluated if required. If f1 is non-nil, then f2 should not be evaluated -- it may have side-effects.

Maybe Clojure already offers such a function (or macro).

EDIT: Here a solution that I came up with (modified from Stuart Halloway's Programming Clojure, (and ...) macro on page 206):

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

Seems to work.

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

Fixed.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

呆萌少年 2024-10-07 14:14:04

你想要的是“或”宏。

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

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

如果您只想要 nil 而不是 false ,请执行以下操作重写 和 并将其命名为合并。

编辑:

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

What you want is the "or" macro.

Evaluates exprs one at a time, from left to right. If a form
returns a logical true value, or returns that value and doesn't
evaluate any of the other expressions, otherwise it returns the
value of the last expression. (or) returns nil.

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

If you only want nil and not false do a rewrite of and and name it coalesce.

Edit:

This could not be done as a function because functions evaluate all their arguments first. This could be done in Haskell because functions are lazy (not 100% sure about the Haskell thing).

南渊 2024-10-07 14:14:04

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

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

Based on nickik's answer and "or" clojure macro:

(defmacro coalesce
    ([] nil)
    ([x] x)
    ([x & next]
       `(let [v# ~x]
           (if (not (nil? v#)) v# (coalesce ~@next)))))
娇妻 2024-10-07 14:14:04

您可以使用 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))

You could use keep introduced in 1.2:

EDIT: extended answer a little bit. Macro for direct invokations. Helper for eg. apply + lazy seq producing the values.

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

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

However to prevent evaluation of the values one needs some home-grown way.

Ugly:

(lazy-cat [e1] [e2] [e3])

A little more involved but prettier in the code:

(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))
尝蛊 2024-10-07 14:14:04

如果您不想使用宏,则合并的某些函数版本:

(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"

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

Some function versions of coalesce, if you'd rather avoid macros:

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

Usage:

=> (coalesce nil "a" "b")
"a"
=> (coalesce-with not-empty nil "" "123")
"123"

Unlike the spec, this will evaluate all args. Use or or another appropriate macro solution if you want short circuiting evaluation.

乖乖 2024-10-07 14:14:04

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

例如:

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

Perhaps I'm misapprehending the question, but isn't this just the first filtered element?

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

It could be shortened up to:

(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
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文