2011-05-26 63 views
19

Clojure中的 - >運算符(以及Clojure中說的這個運算符是什麼?)等價於F#中的管道運算符?>。如果是這樣,爲什麼它需要在這樣一個複雜的宏定義,(|>)只是定義爲- > Clojure中的運算符

let inline (|>) x f = f x 

或者如果沒有,F#的管道運營商存在的Clojure,不然你怎麼會在定義這樣一個操作符Clojure的?

回答

31

不,他們是不一樣的。 Clojure並不需要|>,因爲所有函數調用都包含在列表中,例如(+ 1 2):沒有什麼魔法可以使1 + 2工作處於隔離狀態

->是爲了減少嵌套和簡化常見模式。例如:

(-> x (assoc :name "ted") (dissoc :size) (keys)) 

擴展到

(keys (dissoc (assoc x :name "ted") :size)) 

前者往往更容易閱讀,因爲從概念上講你在x執行一系列操作;前面的代碼就是這樣「塑造」的,而後者需要一些精神上的解開來解決。

你可以編寫一個宏來使這個工作。這個想法是把你的宏包裝在你想要轉換的整個源代碼樹上,然後讓它尋找|>符號;它可以將源代碼轉換成您想要的形狀。 Hiredman用他的functional軟件包以非常好的Haskell編寫代碼。

+0

我明白了。看起來線程運算符在Clojure中很有用,只是因爲Clojure語法的工作方式,但在F#中不會有用。反之亦然,管道運營商。 – 2011-05-26 21:51:44

+1

** Clojure in Action **將第50和51頁中的這些宏的名稱列爲「線程優先」( - >)和「線程最後」( - >>)宏,雖然官方文檔沒有似乎給了他們實際的名字。 – 2012-01-02 03:29:41

11

它被稱爲「線程」運算符。由於性能原因,它被寫爲宏而不是正常函數,因此它可以提供一個很好的語法 - 即它在編譯時應用該變換。

它比你所描述的|>操作符更強大,因爲它旨在通過幾個函數傳遞一個值,其中每個連續值被「插入」爲以下函數調用的第一個參數。這是一個有點做作的例子:

(-> [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 
+1

爲什麼你說' - >'是出於性能原因的宏「?這是一個宏,因爲作爲一個功能它是行不通的。 – amalloy 2011-08-18 18:41:08

+1

即使「 - >」是一個函數,但您可以使用( - > a#(...)#(...)#(...)...)實現類似的結果,但它會很慢並且有些醜陋地使用:-) – mikera 2011-08-18 19:07:48

+1

'( - > b(ac)quote)'如果它不是一個宏,它永遠不會評估爲'(quote(abc))'並且還有其他類似的情況。 '#(...)'情況將涉及手動完成所有已經完成的工作,你只是將它製作成另一個版本的「comp」。 – amalloy 2011-08-19 01:13:46

10

還值得一提的是,有一個->> macro將線程的形式作爲最後一個參數:

(->> a (+ 5) (let [a 5])) 

的Clojure的喜悅,章有關此主題8.1會談位。

3

在閱讀源代碼時(特別是說話時),我總是將->運算符聲明爲「線程優先」,將->>運算符聲明爲「線程最後」。

請注意,現在有一個操作者as->比任一->->>.更靈活的形式爲:

(as-> val name (form1 arg1 name arg2)...) 

val被評估並分配到佔位符符號name,這對用戶可以以下列形式放置在任何位置。我通常爲佔位符符號選擇單詞「it」。我們可以模仿線程第一->像這樣:

user=> (-> :a 
      (vector 1)) 
[:a 1] 
user=> (as-> :a it 
      (vector it 1)) 
[:a 1] 

我們可以模仿線程最後->>像這樣:

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