Поиск…


Вступление

Преобразователи являются составными компонентами для обработки данных независимо от контекста. Таким образом, они могут использоваться для обработки коллекций, потоков, каналов и т. Д. Без знания их источников входного сигнала или выходных стоков.

Ядро библиотеки Clojure было расширено в 1.7, так что последовательность функций, таких как map, filter, take и т. Д., Возвращает преобразователь при вызове без последовательности. Поскольку преобразователи являются функциями с конкретными контрактами, они могут быть составлены с использованием обычной функции comp .

замечания

Преобразователи позволяют контролировать ленивость по мере их потребления. Например, into нетерпение, как и следовало ожидать, но sequence будет лениво потреблять последовательность через преобразователь. Однако гарантия ленивости различна. Достаточно источника будет потребляться для создания элемента изначально:

(take 0 (sequence (map #(do (prn '-> %) %)) (range 5)))
;; -> 0
;; => ()

Или решить, пуст ли список:

(take 0 (sequence (comp (map #(do (prn '-> %) %)) (remove number?)) (range 5)))
;; -> 0
;; -> 1
;; -> 2
;; -> 3
;; -> 4
;; => ()

Что отличается от обычного ленивого поведения последовательности:

(take 0 (map #(do (prn '-> %) %) (range 5)))
;; => ()

Малый преобразователь, примененный к вектору

(let [xf (comp
           (map inc)
           (filter even?))]
  (transduce xf + [1 2 3 4 5 6 7 8 9 10]))
;; => 30

В этом примере создается преобразователь, назначенный локальному xf и использует transduce для применения к некоторым данным. Преобразователь добавляет один к каждому из его входов и возвращает только четные числа.

transduce подобен reduce и сворачивает входную коллекцию на одно значение, используя предоставленную + функцию.

Это читается как последний макрос потока, но отделяет входные данные от вычислений.

(->> [1 2 3 4 5 6 7 8 9 10]
     (map inc)
     (filter even?)
     (reduce +))
;; => 30

Применение преобразователей

(def xf (filter keyword?))

Применить к коллекции, возвращая последовательность:

(sequence xf [:a 1 2 :b :c]) ;; => (:a :b :c)

Применитесь к коллекции, уменьшая полученную коллекцию с помощью другой функции:

(transduce xf str [:a 1 2 :b :c]) ;; => ":a:b:c"

Примените к коллекции и conj результат в другую коллекцию:

(into [] xf [:a 1 2 :b :c]) ;; => [:a :b :c]

Создайте основной асинхронный канал, который использует преобразователь для фильтрации сообщений:

(require '[clojure.core.async :refer [chan >!! <!! poll!]])
(doseq [e [:a 1 2 :b :c]] (>!! ch e))
(<!! ch) ;; => :a
(<!! ch) ;; => :b
(<!! ch) ;; => :c
(poll! ch) ;;=> nil

Создание / использование преобразователей

Поэтому наиболее используемые функции на карте и фильтре Clojure были изменены для возврата преобразователей (составных алгоритмических преобразований), если они не вызываются с коллекцией. Это означает:

(map inc) возвращает преобразователь, и так (filter odd?)

Преимущество: функции могут быть скомпонованы в единую функцию comp, что означает перемещение коллекции только один раз. Сохраняет время работы более чем на 50% в некоторых сценариях.

Определение:

(def composed-fn (comp (map inc) (filter odd?)))

Использование:

;; So instead of doing this:
(->> [1 8 3 10 5]
     (map inc)
    (filter odd?))
;; Output [9 11]

;; We do this: 
(into [] composed-fn [1 8 3 10 5])
;; Output: [9 11]


Modified text is an extract of the original Stack Overflow Documentation
Лицензировано согласно CC BY-SA 3.0
Не связан с Stack Overflow