Ricerca…
introduzione
I trasduttori sono componenti componibili per l'elaborazione di dati indipendentemente dal contesto. In questo modo possono essere utilizzati per elaborare raccolte, flussi, canali, ecc. Senza conoscere le loro sorgenti di input o i sink di output.
La libreria di core Clojure è stata estesa in 1.7 in modo che la sequenza funzioni come map, filter, take, etc. restituisca un trasduttore quando viene chiamato senza una sequenza. Poiché i trasduttori sono funzioni con contratti specifici, possono essere composti utilizzando la normale funzione comp
.
Osservazioni
I trasduttori consentono di controllare la pigrizia man mano che vengono consumati. Ad esempio into
è desideroso come ci si aspetterebbe, ma la sequence
consumerà pigramente la sequenza attraverso il trasduttore. Tuttavia, la garanzia di pigrizia è diversa. Basta la fonte per consumare un elemento inizialmente:
(take 0 (sequence (map #(do (prn '-> %) %)) (range 5)))
;; -> 0
;; => ()
O decidere se l'elenco è vuoto:
(take 0 (sequence (comp (map #(do (prn '-> %) %)) (remove number?)) (range 5)))
;; -> 0
;; -> 1
;; -> 2
;; -> 3
;; -> 4
;; => ()
Che differisce dal solito comportamento a sequenza pigra:
(take 0 (map #(do (prn '-> %) %) (range 5)))
;; => ()
Piccolo trasduttore applicato a un vettore
(let [xf (comp
(map inc)
(filter even?))]
(transduce xf + [1 2 3 4 5 6 7 8 9 10]))
;; => 30
Questo esempio crea un trasduttore assegnato al xf
locale e usa transduce
per applicarlo ad alcuni dati. Il trasduttore aggiunge uno a ciascuno dei suoi ingressi e restituisce solo i numeri pari.
transduce
è come reduce
e collassa la raccolta di input su un valore singolo usando la funzione +
fornita.
Questo si legge come l'ultima macro del thread, ma separa i dati di input dai calcoli.
(->> [1 2 3 4 5 6 7 8 9 10]
(map inc)
(filter even?)
(reduce +))
;; => 30
Applicazione dei trasduttori
(def xf (filter keyword?))
Applica a una raccolta, restituendo una sequenza:
(sequence xf [:a 1 2 :b :c]) ;; => (:a :b :c)
Applica a una raccolta, riducendo la raccolta risultante con un'altra funzione:
(transduce xf str [:a 1 2 :b :c]) ;; => ":a:b:c"
Applica a una raccolta e conj
il risultato a un'altra raccolta:
(into [] xf [:a 1 2 :b :c]) ;; => [:a :b :c]
Crea un canale asincrono principale che utilizza un trasduttore per filtrare i messaggi:
(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
Creazione / uso di trasduttori
Quindi le funzioni più utilizzate sulla mappa e sul filtro Clojure sono state modificate per restituire trasduttori (trasformazioni algoritmiche componibili), se non chiamate con una raccolta. Questo significa:
(map inc)
restituisce un trasduttore e lo fa (filter odd?)
Il vantaggio: le funzioni possono essere composte in una singola funzione da comp, il che significa attraversare la raccolta una sola volta. Risparmia tempo di esecuzione di oltre il 50% in alcuni scenari.
Definizione:
(def composed-fn (comp (map inc) (filter odd?)))
Uso:
;; 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]