-> оператор в Clojure - PullRequest
       36

-> оператор в Clojure

22 голосов
/ 27 мая 2011

Является ли оператор -> в Clojure (и как этот оператор называется в языке Clojure?) Эквивалентным оператору конвейера |> в F #?Если так, то зачем ему такое сложное макроопределение, когда (|>) просто определяется как

let inline (|>) x f = f x

Или, если нет, существует ли оператор конвейера F # в Clojure, или как вы определяете такойоператор в Clojure?

Ответы [ 4 ]

32 голосов
/ 27 мая 2011

Нет, они не одинаковы. Clojure на самом деле не нуждается в |>, потому что все вызовы функций заключены в списки, такие как (+ 1 2): нет никакого волшебства, которое вы могли бы сделать, чтобы 1 + 2 работал в изоляции . 1

-> для уменьшения вложенности и упрощения общих шаблонов. Например:

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

Расширяется до

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

Первый часто легче читать, потому что концептуально вы выполняете серию операций над x; первый код «формируется» таким образом, в то время как второй нуждается в некотором умственном разгадке, чтобы сработать.

1 Вы можете написать макрос, который выполняет эту работу. Идея состоит в том, чтобы обернуть ваш макрос вокруг всего исходного дерева, которое вы хотите преобразовать, и позволить ему искать |> символов; затем он может преобразовать источник в нужную вам форму. Hiredman сделал возможным написание кода очень похожим на Haskell образом с помощью своего функционального пакета.

11 голосов
/ 27 мая 2011

Это называется оператором "поток". Он написан как макрос, а не как обычная функция из соображений производительности, и поэтому может обеспечить хороший синтаксис - то есть он применяет преобразование во время компиляции.

Он несколько более мощный, чем описанный вами оператор |>, поскольку он предназначен для передачи значения через несколько функций, где каждое последующее значение «вставляется» в качестве первого параметра следующих вызовов функций. Вот несколько надуманный пример:

(-> [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
10 голосов
/ 27 мая 2011

Стоит также отметить, что существует - >> макрос , который передаст форму как последний аргумент:

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

Радость Clojure, глава 8.1, немного говорит об этом предмете.

5 голосов
/ 12 июля 2014

При чтении исходного кода (особенно при разговоре) я всегда объявляю оператор -> как "первый поток", а оператор ->> как "последний поток".

Имейте в видучто теперь существует оператор 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]

Мы можем имитировать последний поток ->> следующим образом:

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