代码之家  ›  专栏  ›  技术社区  ›  Charles Duffy

->/->>管道中的条件元素

  •  15
  • Charles Duffy  · 技术社区  · 12 年前

    给定 ->> 类似管道:

    (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 identity 调用),但实际上无法编译。

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

    5 回复  |  直到 12 年前
        1
  •  16
  •   Dave Liepmann Marc    3 年前

    Modern Clojure(也就是说,从1.5开始)支持各种条件线程选项,但您可能想要 cond->>

    带的条件线程 cond-> 第二->>

    Clojure优惠 cond-> cond->> ,每个请求一组对:一个测试和一个表达式,如果该测试的结果为true。这些与 cond 但不要停留在第一次真正的测试上。

    (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
    

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

        2
  •  12
  •   Charles Duffy    12 年前

    这也很有效:

    (defn my-fn [{:keys [foo bar baz]}]
      (->> (get-data)
           (#(if foo (do-foo %) %))
           (#(if bar (do-bar %) %))
           (#(if baz (do-baz %) %)) ))
    
        3
  •  9
  •   Hugo Duncan    12 年前

    您可能对这些宏感兴趣 https://github.com/pallet/thread-expr

        4
  •  6
  •   Arthur Ulfeldt    12 年前

    你可以修复编译部分(尽管不是苦行僧部分) 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       )
    
        5
  •  5
  •   Alex Taggart    12 年前

    如果你经常需要这种东西,还有一种更通用的方法:

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