clojure
Przetworniki
Szukaj…
Wprowadzenie
Przetworniki są komponowalnymi komponentami do przetwarzania danych niezależnie od kontekstu. Dzięki temu można ich używać do przetwarzania kolekcji, strumieni, kanałów itp. Bez znajomości ich źródeł wejściowych lub odbiorników wyjściowych.
Biblioteka rdzenia Clojure została rozszerzona w wersji 1.7, dzięki czemu funkcje sekwencji, takie jak mapowanie, filtrowanie, pobieranie itp. Zwracają przetwornik, gdy są wywoływane bez sekwencji. Ponieważ przetworniki są funkcjami o określonych kontraktach, można je komponować przy użyciu normalnej funkcji comp
.
Uwagi
Przetworniki pozwalają kontrolować lenistwo podczas ich konsumpcji. Na przykład into
jest chętny, jak można się spodziewać, ale sequence
leniwie zużywa sekwencję przez przetwornik. Jednak gwarancja lenistwa jest inna. Dość źródła zostanie zużyte na początkowy element:
(take 0 (sequence (map #(do (prn '-> %) %)) (range 5)))
;; -> 0
;; => ()
Lub zdecyduj, czy lista jest pusta:
(take 0 (sequence (comp (map #(do (prn '-> %) %)) (remove number?)) (range 5)))
;; -> 0
;; -> 1
;; -> 2
;; -> 3
;; -> 4
;; => ()
Który różni się od zwykłego zachowania leniwej sekwencji:
(take 0 (map #(do (prn '-> %) %) (range 5)))
;; => ()
Mały przetwornik zastosowany do wektora
(let [xf (comp
(map inc)
(filter even?))]
(transduce xf + [1 2 3 4 5 6 7 8 9 10]))
;; => 30
Ten przykład tworzy przetwornik przypisany do lokalnego xf
i używa transduce
do zastosowania go do niektórych danych. Przetwornik dodaje jeden do każdego z wejść i zwraca tylko liczby parzyste.
transduce
jest jak reduce
i zwija kolekcję danych wejściowych do pojedynczej wartości za pomocą dostarczonej funkcji +
.
Odczytuje to jak makro ostatniego wątku, ale oddziela dane wejściowe od obliczeń.
(->> [1 2 3 4 5 6 7 8 9 10]
(map inc)
(filter even?)
(reduce +))
;; => 30
Zastosowanie przetworników
(def xf (filter keyword?))
Zastosuj do kolekcji, zwracając sekwencję:
(sequence xf [:a 1 2 :b :c]) ;; => (:a :b :c)
Zastosuj do kolekcji, zmniejszając wynikową kolekcję za pomocą innej funkcji:
(transduce xf str [:a 1 2 :b :c]) ;; => ":a:b:c"
Zastosuj do kolekcji i conj
wynik w inną kolekcję:
(into [] xf [:a 1 2 :b :c]) ;; => [:a :b :c]
Utwórz główny kanał asynchroniczny, który używa przetwornika do filtrowania wiadomości:
(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
Tworzenie / używanie przetworników
Dlatego najczęściej używane funkcje na mapie Clojure i filtrze zostały zmodyfikowane, aby zwracały przetworniki (składane transformacje algorytmiczne), jeśli nie zostały wywołane z kolekcją. To znaczy:
(map inc)
zwraca przetwornik, podobnie jak (filter odd?)
Zaleta: funkcje można połączyć w jedną funkcję przez comp, co oznacza, że przemierza się kolekcję tylko raz. Oszczędza czas pracy o ponad 50% w niektórych scenariuszach.
Definicja:
(def composed-fn (comp (map inc) (filter odd?)))
Stosowanie:
;; 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]