【发布时间】:2011-09-02 22:51:58
【问题描述】:
Clojure 中的 -> 运算符(在 Clojure 中这个运算符是什么?)是否等同于 F# 中的管道运算符 |>?如果是这样,为什么需要这么复杂的宏定义,当(|>)只是定义为
let inline (|>) x f = f x
如果没有,Clojure 中是否存在 F# 的管道运算符,或者您将如何在 Clojure 中定义这样的运算符?
【问题讨论】:
Clojure 中的 -> 运算符(在 Clojure 中这个运算符是什么?)是否等同于 F# 中的管道运算符 |>?如果是这样,为什么需要这么复杂的宏定义,当(|>)只是定义为
let inline (|>) x f = f x
如果没有,Clojure 中是否存在 F# 的管道运算符,或者您将如何在 Clojure 中定义这样的运算符?
【问题讨论】:
不,它们不一样。 Clojure 并不真正需要 |>,因为所有函数调用都包含在列表中,例如 (+ 1 2):没有什么魔法可以让 1 + 2 单独工作。1
-> 用于减少嵌套和简化常见模式。例如:
(-> x (assoc :name "ted") (dissoc :size) (keys))
扩展到
(keys (dissoc (assoc x :name "ted") :size))
前者通常更容易阅读,因为从概念上讲,您正在对x 执行一系列操作;前者的代码是这样“塑造”的,而后者需要一些精神上的解开才能解决。
1 你可以编写一个宏来完成这项工作。这个想法是把你的宏包裹在你想要转换的整个源代码树周围,让它寻找|>符号;然后它可以将源转换为您想要的形状。 Hiredman 使用他的 functional 包,使以非常类似于 Haskell 的方式编写代码成为可能。
【讨论】:
它被称为“线程”运算符。出于性能原因,它被写成宏而不是普通函数,因此它可以提供一个很好的语法 - 即它在编译时应用转换。
它比您描述的 |> 运算符更强大,因为它旨在通过多个函数传递一个值,其中每个连续值作为以下函数调用的第一个参数“插入”。这是一个有些人为的例子:
(-> [1]
(concat [2 3 4])
(sum)
((fn [x] (+ x 100.0))))
=> 110.0
如果你想定义一个与你描述的 F# 运算符完全一样的函数,你可以这样做:
(defn |> [x f] (f x))
(|> 3 inc)
=> 4
不确定它到底有多大用处,但无论如何你都在那里 :-)
最后,如果你想通过一系列函数传递一个值,你总是可以在 clojure 中执行如下操作:
(defn pipeline [x & fns]
((apply comp fns) x))
(pipeline 1 inc inc inc inc)
=> 5
【讨论】:
->是一个“出于性能原因”的宏?这是一个宏,因为作为一个函数它不起作用。
(-> b (a c) quote) 如果不是宏,则永远无法评估为(quote (a b c)),并且还有其他类似的情况。 #(...) 案例将涉及手动完成 -> 已经完成的所有工作;你只会把它变成另一个版本的comp。
【讨论】:
在阅读源代码时(尤其是说话时),我总是将-> 运算符发音为“thread-first”,将->> 运算符发音为“thread-last”。
请记住,现在有一个运算符as->,它比-> 或->>. 更灵活,形式为:
(as-> val name (form1 arg1 name arg2)...)
值val 被评估并分配给占位符符号name,用户可以将其放置在以下表格中的任何位置。我通常选择“它”作为占位符符号。我们可以像这样模仿线程优先->:
user=> (-> :a
(vector 1))
[:a 1]
user=> (as-> :a it
(vector it 1) )
[:a 1]
我们可以像这样模仿 thread-last ->>:
user=> (->> :a
(vector 2))
[2 :a]
user=> (as-> :a it
(vector 2 it) )
[2 :a]
或者,我们可以将它们组合成一个表达式:
user=> (as-> :a it
(vector it 1)
(vector 2 it))
[2 [:a 1]]
user=> (as-> :a it
(vector it 1)
(vector 2 it)
(vector "first" it "last"))
["first" [2 [:a 1]] "last"]
我经常使用最后一种形式,所以我在the Tupelo Library 中创建了一个新的运算符it->:
(it-> 1
(inc it) ; thread-first or thread-last
(+ it 3) ; thread-first
(/ 10 it) ; thread-last
(str "We need to order " it " items." ) ; middle of 3 arguments
;=> "We need to order 2 items." )
【讨论】: