clojure
преобразователи
Поиск…
Вступление
Преобразователи являются составными компонентами для обработки данных независимо от контекста. Таким образом, они могут использоваться для обработки коллекций, потоков, каналов и т. Д. Без знания их источников входного сигнала или выходных стоков.
Ядро библиотеки 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]