Clojure ->;中的条件元素-&燃气轮机&燃气轮机;管道

Clojure ->;中的条件元素-&燃气轮机&燃气轮机;管道,clojure,Clojure,给定一个类似这样的->管道: (defn my-fn [] (->> (get-data) (do-foo) (do-bar) (do-baz))) 我希望使各个阶段都有条件 我想到的第一种写作方式是: (defn my-fn [{:keys [foo bar baz]}] (->> (get-data) (if foo (do-foo) identity) (if bar (do-bar)

给定一个类似这样的
->
管道:

(defn my-fn []
  (->> (get-data)
       (do-foo)
       (do-bar)
       (do-baz)))
我希望使各个阶段都有条件

我想到的第一种写作方式是:

(defn my-fn [{:keys [foo bar baz]}]
  (->> (get-data)
       (if foo (do-foo) identity)
       (if bar (do-bar) identity)
       (if baz (do-baz) identity))
但是,由于
->
宏试图插入到
if
表单中,这不仅在性能方面看起来很不幸(具有noop
标识
调用),而且实际上无法编译


写这篇文章的合适的、相当枯燥的方式是什么?

您可以修复编译部分(虽然不是苦行部分),方法是让if语句通过放置另一组
()
来决定运行哪个函数:

(defn my-fn [{:keys [foo bar baz]}]
  (->> (get-data)
   ((if foo do-foo identity))
   ((if bar do-bar identity))
   ((if baz do-baz identity)))
将扩展为一系列调用,如下所示:

   ;  choose function         and call it    
   ( (if foo do-foo identity) args       )
   ( (if bar do-bar identity) args       )
   ( (if baz do-baz identity) args       )
这也有效:

(defn my-fn [{:keys [foo bar baz]}]
  (->> (get-data)
       (#(if foo (do-foo %) %))
       (#(if bar (do-bar %) %))
       (#(if baz (do-baz %) %)) ))

如果你经常需要这类东西,一个更一般的方法是:

(defn comp-opt [& flag-fns] (->> flag-fns (partition 2) (filter first) (map second) (apply comp))) (defn my-fn [{:keys [foo bar baz]}] (map (comp-opt foo do-foo bar do-bar baz do-baz) (get-data))) (定义组件选项[&标记fns] (->标志fns (第2分区) (先过滤) (地图二) (应用comp))) (defn my fn[{:keys[foo-bar-baz]}] (地图(comp opt foo do foo) 酒吧 巴兹多巴兹) (获取数据)
您可能对这些宏感兴趣

现代Clojure(即从1.5开始)支持各种条件线程选项,但您可能需要
cond->

使用
cond->
cond->
Clojure提供and,每个and都要求一组对:一个测试和一个表达式(如果该测试的计算结果为true)。这与
cond
非常相似,但不会在第一个true测试时停止

(cond->> 5
  true inc
  false inc
  nil inc)
=> 6
您的具体示例最好这样写:

(defn my-fn [{:keys [foo bar baz]}]
  (cond->> (get-data)
    foo (do-foo)
    bar (do-bar)
    baz (do-baz)))
as-> 值得一提的是,它可能是最通用的线程宏。它为您提供了一个名称,用于表示通过窗体线程化的“对象”。例如:

(as-> 0 n
  (inc n)
  (if false
    (inc n)
    n))
=> 1

(as-> 0 n
  (inc n)
  (if true
    (inc n)
    n))
=> 2

这为混合使用需要在参数列表中的不同点(即从->切换到->>语法)线程化表达式的函数提供了极大的灵活性为了代码的可读性,应该避免使用无关的命名变量,但通常这是表示过程的最清晰、最简单的方法。

是否像通常与
->
一起使用的那样,
获取数据
返回序列?函数调用是副作用,还是获取/返回某些东西?它是ems线程调用中的if不太可能在任何可感知的情况下影响性能way@AlexTaggart是的,
get data
返回一个序列——可能是一个相当长的序列,这实际上是一个内部循环。
do-*
函数是纯函数,没有副作用;也许我在示例中对代码的命名很差。@CharlesDuffy arthur在性能方面是正确的。分支检查将很快优化。@AlexTaggart我实际上更关心身份调用,而不是分支。+1建议不要重新发明轮子。:)我使用了你的thread expr库,它简单、小、优雅且工作正常。你建议
(when->foo-do-foo)
,或其他用法?谢谢!我一直在引用我的FN,即
((如果foo'do-foo'标识)args)
-您的示例帮助我调试了我的问题!虽然
as->
是最通用的,但IMO
cond->
是最适合OPs的requirement@Andy你完全正确,不知道我为什么错过了。更新了答案。