Clojure凝聚函数

Clojure凝聚函数,clojure,coalesce,Clojure,Coalesce,SQL提供了一个名为coalesce(a,b,c,…)的函数,如果其所有参数都为null,则返回null,否则返回第一个非null参数 你会如何在Clojure写这样的东西 它的名称如下:(合并f1 f2 f3…,其中fi是形式,只有在需要时才应进行评估。如果f1为非零,则不应评估f2——它可能有副作用 也许Clojure已经提供了这样一个函数(或宏) 编辑:这里是我提出的一个解决方案(修改自Stuart Halloway的编程Clojure,(和…宏,第206页): 似乎有效 (defmacr

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

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

它的名称如下:
(合并f1 f2 f3…
,其中
fi
是形式,只有在需要时才应进行评估。如果
f1
为非零,则不应评估
f2
——它可能有副作用

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

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

似乎有效

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

您需要的是“或”宏

从左到右一次计算一个表达式。如果是表格 返回逻辑真值,或返回该值而不返回 对任何其他表达式求值,否则返回 最后一个表达式的值。(或)返回零

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

编辑:


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

您可以使用1.2中介绍的keep:

编辑:稍微扩展一下答案。用于直接调用的宏。例如,apply+lazy seq的助手生成值

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

(defmacro coalesce
  [& values]
  `(coalesce* (lazy-list ~@values)))
然而,为了防止对价值观的评估,我们需要一些本土的方法

丑陋的:

代码中有一点更复杂但更漂亮:

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

根据nickik的回答和“或”clojure宏:

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

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

例如:

user=>(第一个(过滤器(补码nil?[nil false:foo])) 假的 user=>(第一个(过滤器(补码nil?[nil:foo])) :foo 用户=>(第一个(过滤器(补零?[])) 无 用户=>(第一个(过滤器(补码为零?)为零) 无 可缩短至:

(defn coalesce [& vals] (first (filter (complement nil?) vals))) (defn合并[&VAL] (第一个(过滤器(补码为零?)VAL))) user=>(合并为nil false:foo) 假的 用户=>(合并零:foo) :foo 用户=>(合并为零) 无 用户=>(合并) 无
如果您希望避免使用宏,请选择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"

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

我需要第一个非零值,而不是最后一个,但是
(或…
满足我的需要。我没有意识到
(and…
(or…
返回值。我认为他们的回答是假是真。但是即使是这些也不会返回我想要的输入值
false
。哦,我肯定会更改它。@user128186:不确定您是否需要
(not(nil?v#))
(if…
语句中的
,因为任何不是
false
nil
的值都会计算为
true
。否则,我们的解决方案是相同的。你为什么要对“or”-宏进行1:1重写?@Ralph:那么你希望first不是nil或first不是false值?@ajan:很好!我想我需要显式地测试
nil
。@nickik::-)没有意识到我有。我知道为什么非宏解决方案可能更好,因为宏解决方案不能与其他函数组合。@ralph:当然,接受的解决方案更快,但我的解决方案更灵活。你应该选择什么取决于你的需要。如果您不需要速度,但有一个懒洋洋创建的序列要合并,那么我的解决方案就是这样。如果您需要快速处理少数已知值。然后是阿扬的救援方案。YMMV.)我不是在批评。不管怎么说,这更像是一项学术活动。我在考虑如何在Scala中实现“elvis”操作符,这让我想到了Clojure中类似的东西。这是另一种seq-y方法。第三个是
(第一个(删除nil?…)
。但是,这并不能解决要合并的表达式只能根据需要进行计算的问题。对于像
(重复地#(生成某个东西))
这样的东西,这在盒子的底部有效,但对于“文字”值无效:
[(做某件事)(做其他事)(做第三件事)]
。在这里,所有的东西都是在过滤器看到之前进行评估的。是的,我错过了args需求的延迟评估。 (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
(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"