Pertanyaan Dapatkah seseorang menjelaskan Transduser Clojure kepada saya dalam istilah yang sederhana?


Saya telah mencoba membaca tentang ini tetapi saya masih tidak mengerti nilai dari mereka atau apa yang mereka gantikan. Dan apakah mereka membuat kode saya lebih pendek, lebih bisa dimengerti atau apa?

Memperbarui

Banyak orang yang mengirim jawaban, tetapi alangkah baiknya melihat contoh dengan dan tanpa transduser untuk sesuatu yang sangat sederhana, yang bahkan seorang idiot seperti saya dapat pahami. Kecuali tentu saja transduser memerlukan pemahaman tingkat tinggi tertentu, dalam hal ini saya tidak akan pernah mengerti mereka :(


80
2017-10-11 17:31


asal


Jawaban:


Transduser adalah resep yang harus dilakukan dengan urutan data tanpa pengetahuan apa urutan yang mendasari (bagaimana melakukannya). Ini bisa berupa seq, async channel atau mungkin bisa diobservasi.

Mereka adalah composable dan polymorphic.

Keuntungannya adalah, Anda tidak perlu menerapkan semua kombinator standar setiap kali sumber data baru ditambahkan. Lagi dan lagi. Akibat yang ditimbulkan Anda sebagai pengguna dapat menggunakan kembali resep tersebut pada sumber data yang berbeda.

Pembaruan Iklan

Sebelum versi 1.7 dari Clojure Anda memiliki tiga cara bagaimana menulis query dataflow:

  1. panggilan bertingkat
    (reduce + (filter odd? (map #(+ 2 %) (range 0 10))))
  1. komposisi fungsional
    (def xform
      (comp
        (partial filter odd?)
        (partial map #(+ 2 %))))
    (reduce + (xform (range 0 10)))
  1. threading macro
    (defn xform [xs]
      (->> xs
           (map #(+ 2 %))
           (filter odd?)))
    (reduce + (xform (range 0 10)))

Dengan transduser Anda akan menulisnya seperti:

(def xform
  (comp
    (map #(+ 2 %))
    (filter odd?)))
(transduce xform + (range 0 10))

Mereka semua melakukan hal yang sama. Perbedaannya adalah Anda tidak pernah memanggil Transduser secara langsung, Anda meneruskannya ke fungsi lain. Transduser tahu apa yang harus dilakukan, fungsi yang membuat transduser tahu caranya. Urutan kombinatori seperti Anda menulisnya dengan threading macro (urutan alami). Sekarang Anda dapat menggunakannya kembali xform dengan saluran:

(chan 1 xform)

67
2017-10-12 07:33



Transduser meningkatkan efisiensi, dan memungkinkan Anda menulis kode yang efisien dengan cara yang lebih modular.

Ini layak dijalankan.

Dibandingkan dengan membuat panggilan ke yang lama map, filter, reduce dll. Anda mendapatkan kinerja yang lebih baik karena Anda tidak perlu membuat koleksi menengah di antara setiap langkah, dan berulang kali berjalani koleksi tersebut.

Dibandingkan dengan reducers, atau secara manual menyusun semua operasi Anda menjadi satu ekspresi, Anda lebih mudah menggunakan abstraksi, modularitas yang lebih baik, dan penggunaan kembali fungsi pemrosesan.


37
2017-10-11 18:41



Transduser adalah alat kombinasi untuk mengurangi fungsi.

Contoh: Mengurangi fungsi adalah fungsi yang mengambil dua argumen: Hasil sejauh ini dan masukan. Mereka mengembalikan hasil baru (sejauh ini). Sebagai contoh +: Dengan dua argumen, Anda dapat memikirkan yang pertama sebagai hasil sejauh ini dan yang kedua sebagai input.

Transduser sekarang dapat mengambil fungsi + dan menjadikannya fungsi dua kali plus (menggandakan setiap input sebelum menambahkannya). Ini adalah bagaimana transduser itu akan terlihat (dalam kebanyakan istilah dasar):

(defn double
  [rfn]
  (fn [r i] 
    (rfn r (* 2 i))))

Untuk pengganti ilustrasi rfn dengan + untuk melihat caranya + diubah menjadi dua kali-plus:

(def twice-plus ;; result of (double +)
  (fn [r i] 
    (+ r (* 2 i))))

(twice-plus 1 2)  ;-> 5
(= (twice-plus 1 2) ((double +) 1 2)) ;-> true

Begitu

(reduce (double +) 0 [1 2 3]) 

sekarang akan menghasilkan 12.

Mengurangi fungsi yang dikembalikan oleh transduser tidak bergantung pada bagaimana hasil diakumulasikan karena mereka terakumulasi dengan fungsi pengurangan yang diteruskan ke mereka, tanpa sepengetahuan bagaimana. Di sini kami gunakan conj dari pada +. Conj mengambil koleksi dan nilai dan mengembalikan koleksi baru dengan nilai yang ditambahkan.

(reduce (double conj) [] [1 2 3]) 

akan menghasilkan [2 4 6]

Mereka juga independen dari jenis sumber apa inputnya.

Beberapa transduser dapat dirangkai sebagai resep (rantai) untuk mengubah fungsi-fungsi pereduksi.

Pembaruan: Karena sekarang ada laman resmi tentangnya, saya sangat menyarankan untuk membacanya: http://clojure.org/transducers


20
2017-10-12 09:50



Katakanlah Anda ingin menggunakan serangkaian fungsi untuk mengubah aliran data. Shell Unix memungkinkan Anda melakukan hal semacam ini dengan operator pipa, mis.

cat /etc/passwd | tr '[:lower:]' '[:upper:]' | cut -d: -f1| grep R| wc -l

(Perintah di atas menghitung jumlah pengguna dengan huruf r dalam huruf besar atau kecil dalam nama pengguna mereka). Ini diimplementasikan sebagai satu set proses, yang masing-masing membaca dari output proses sebelumnya, jadi ada empat aliran menengah. Anda dapat membayangkan implementasi yang berbeda yang menyusun kelima perintah menjadi satu perintah agregat, yang akan membaca dari inputnya dan menulis outputnya tepat satu kali. Jika aliran menengah mahal, dan komposisinya murah, itu bisa menjadi trade-off yang bagus.

Hal yang sama berlaku untuk Clojure. Ada beberapa cara ringkas untuk mengekspresikan saluran transformasi, tetapi tergantung bagaimana Anda melakukannya, Anda bisa berakhir dengan aliran menengah yang lewat dari satu fungsi ke fungsi berikutnya. Jika Anda memiliki banyak data, lebih cepat untuk menyusun fungsi-fungsi itu menjadi satu fungsi. Transduser memudahkan untuk melakukan itu. Inovasi Clojure sebelumnya, reduksi, membiarkan Anda melakukannya juga, tetapi dengan beberapa pembatasan. Transduser menghapus beberapa batasan tersebut.

Jadi untuk menjawab pertanyaan Anda, transduser tidak akan selalu membuat kode Anda lebih pendek atau lebih mudah dimengerti, tetapi kode Anda mungkin tidak akan lebih panjang atau kurang dapat dipahami, dan jika Anda bekerja dengan banyak data, transduser dapat membuat kode Anda lebih cepat.

Ini adalah gambaran transduser yang cukup bagus.


18
2017-10-11 22:18



Rich Hickey memberikan ceramah 'Transduser' pada konferensi Strange Loop 2014 (45 menit).

Dia menjelaskan dengan cara sederhana apa transduser, dengan contoh dunia nyata - memproses tas di bandara. Dia dengan jelas memisahkan aspek yang berbeda dan mengontraskannya dengan pendekatan saat ini. Menjelang akhir, dia memberikan alasan untuk keberadaan mereka.

Video: https://www.youtube.com/watch?v=6mTbuzafcII


9
2017-10-16 10:52



Saya telah menemukan contoh pembacaan dari transduser-js membantu saya memahaminya dalam hal konkret bagaimana saya dapat menggunakannya dalam kode sehari-hari.

Misalnya, perhatikan contoh ini (diambil dari README pada tautan di atas):

var t = require("transducers-js");

var map    = t.map,
    filter = t.filter,
    comp   = t.comp,
    into   = t.into;

var inc    = function(n) { return n + 1; };
var isEven = function(n) { return n % 2 == 0; };
var xf     = comp(map(inc), filter(isEven));

console.log(into([], xf, [0,1,2,3,4])); // [2,4]

Untuk satu, menggunakan xf terlihat jauh lebih bersih daripada alternatif biasa dengan Underscore.

_.filter(_.map([0, 1, 2, 3, 4], inc), isEven);

8
2017-10-11 18:48



Fungsi transduser (untuk pemahaman saya!) Yang mengambil satu mengurangi berfungsi dan kembali lagi. Fungsi pengurangan adalah salah satunya

Sebagai contoh:

user> (def my-transducer (comp count filter))
#'user/my-transducer
user> (my-transducer even? [0 1 2 3 4 5 6])
4
user> (my-transducer #(< 3 %) [0 1 2 3 4 5 6])
3

Dalam hal ini my-transducer mengambil fungsi penyaringan input yang berlaku untuk 0 kemudian jika nilainya sama? dalam kasus pertama filter melewatkan nilai itu ke counter, lalu menyaring nilai berikutnya. Daripada memfilter terlebih dahulu dan kemudian meneruskan semua nilai tersebut untuk dihitung.

Ini adalah hal yang sama dalam contoh kedua, ia memeriksa satu nilai pada suatu waktu dan jika nilainya kurang dari 3 maka memungkinkan menghitung menambahkan 1.


6
2017-10-14 17:18



Definisi jelas transduser ada di sini:

Transducers are a powerful and composable way to build algorithmic transformations that you can reuse in many contexts, and they’re coming to Clojure core and core.async.

Untuk memahaminya, mari kita pertimbangkan contoh sederhana berikut ini:

;; The Families in the Village

(def village
  [{:home :north :family "smith" :name "sue" :age 37 :sex :f :role :parent}
   {:home :north :family "smith" :name "stan" :age 35 :sex :m :role :parent}
   {:home :north :family "smith" :name "simon" :age 7 :sex :m :role :child}
   {:home :north :family "smith" :name "sadie" :age 5 :sex :f :role :child}

   {:home :south :family "jones" :name "jill" :age 45 :sex :f :role :parent}
   {:home :south :family "jones" :name "jeff" :age 45 :sex :m :role :parent}
   {:home :south :family "jones" :name "jackie" :age 19 :sex :f :role :child}
   {:home :south :family "jones" :name "jason" :age 16 :sex :f :role :child}
   {:home :south :family "jones" :name "june" :age 14 :sex :f :role :child}

   {:home :west :family "brown" :name "billie" :age 55 :sex :f :role :parent}
   {:home :west :family "brown" :name "brian" :age 23 :sex :m :role :child}
   {:home :west :family "brown" :name "bettie" :age 29 :sex :f :role :child}

   {:home :east :family "williams" :name "walter" :age 23 :sex :m :role :parent}
   {:home :east :family "williams" :name "wanda" :age 3 :sex :f :role :child}])

Bagaimana kalau kita ingin tahu berapa banyak anak-anak di desa? Kita dapat dengan mudah menemukannya dengan peredam berikut:

;; Example 1a - using a reducer to add up all the mapped values

(def ex1a-map-children-to-value-1 (r/map #(if (= :child (:role %)) 1 0)))

(r/reduce + 0 (ex1a-map-children-to-value-1 village))
;;=>
8

Inilah cara lain untuk melakukannya:

;; Example 1b - using a transducer to add up all the mapped values

;; create the transducers using the new arity for map that
;; takes just the function, no collection

(def ex1b-map-children-to-value-1 (map #(if (= :child (:role %)) 1 0)))

;; now use transduce (c.f r/reduce) with the transducer to get the answer 
(transduce ex1b-map-children-to-value-1 + 0 village)
;;=>
8

Selain itu, sangat kuat ketika mengambil subkelompok dalam akun juga. Sebagai contoh, jika kita ingin tahu berapa banyak anak-anak dalam Brown Family, kita dapat mengeksekusi:

;; Example 2a - using a reducer to count the children in the Brown family

;; create the reducer to select members of the Brown family
(def ex2a-select-brown-family (r/filter #(= "brown" (string/lower-case (:family %)))))

;; compose a composite function to select the Brown family and map children to 1
(def ex2a-count-brown-family-children (comp ex1a-map-children-to-value-1 ex2a-select-brown-family))

;; reduce to add up all the Brown children
(r/reduce + 0 (ex2a-count-brown-family-children village))
;;=>
2

Saya harap Anda dapat menemukan contoh-contoh ini bermanfaat. Anda dapat menemukan lebih banyak sini

Semoga itu membantu.

Clemencio Morales Lucas.


5
2017-10-16 11:01



Saya membuat blog tentang ini dengan clojurescript contoh yang menjelaskan bagaimana fungsi urutan sekarang dapat diperluas dengan mampu menggantikan fungsi pengurangan.

Ini adalah titik transduser saat saya membacanya. Jika Anda memikirkan tentang itu cons atau conj operasi yang sulit dikodekan dalam operasi seperti map, filter dll., fungsi pengurangan tidak dapat dicapai.

Dengan transduser, fungsi pengurangan dipisahkan dan saya dapat menggantinya seperti yang saya lakukan dengan array javascript asli push terima kasih kepada transduser.

(transduce (filter #(not (.hasOwnProperty prevChildMapping %))) (.-push #js[]) #js [] nextKeys)

filter dan teman-teman memiliki operasi 1 arity baru yang akan mengembalikan fungsi transduksi yang dapat Anda gunakan untuk menyediakan fungsi pengurangan Anda sendiri.


4
2017-12-19 17:24



Inilah jawaban saya (kebanyakan) jargon dan kode gratis.

Pikirkan data dalam dua cara, aliran (nilai yang terjadi dari waktu ke waktu seperti peristiwa) atau struktur (data yang ada pada suatu titik waktu seperti daftar, vektor, array dll).

Ada operasi tertentu yang mungkin ingin Anda lakukan di atas aliran atau struktur. Salah satu operasi tersebut adalah pemetaan. Fungsi pemetaan mungkin menambah setiap item data (dengan asumsi itu adalah angka) sebesar 1 dan Anda dapat dengan mudah membayangkan bagaimana ini bisa diterapkan ke aliran atau struktur.

Fungsi pemetaan hanyalah salah satu dari kelas fungsi yang kadang-kadang disebut sebagai "fungsi mengurangi". Fungsi pengurangan umum lainnya adalah filter yang menghapus nilai yang cocok dengan predikat (misalnya menghapus semua nilai yang genap).

Transduser memungkinkan Anda "membungkus" urutan satu atau beberapa fungsi pengurangan dan menghasilkan "paket" (yang merupakan fungsi itu sendiri) yang berfungsi pada kedua aliran atau struktur. Sebagai contoh, Anda bisa "mengemas" urutan pengurangan fungsi (mis. Menyaring nomor genap, kemudian memetakan angka yang dihasilkan untuk meningkatkannya dengan 1) dan kemudian menggunakan "paket" transduser itu pada aliran atau struktur nilai (atau keduanya) .

Jadi apa yang spesial tentang ini? Biasanya, mengurangi fungsi tidak dapat dikomposisikan secara efisien untuk bekerja pada kedua aliran dan struktur.

Jadi manfaatnya bagi Anda adalah bahwa Anda dapat memanfaatkan pengetahuan Anda di sekitar fungsi-fungsi ini dan menerapkannya ke lebih banyak kasus penggunaan. Biaya untuk Anda adalah bahwa Anda harus belajar beberapa mesin tambahan (yaitu transduser) untuk memberi Anda kekuatan ekstra ini.


1
2017-11-21 23:43