ใครช่วยอธิบาย Clojure Transducers ให้ฉันฟังด้วยคำง่ายๆได้ไหม


101

ฉันได้ลองอ่านสิ่งนี้แล้วแต่ยังไม่เข้าใจคุณค่าของสิ่งเหล่านี้หรือสิ่งที่พวกเขาแทนที่ พวกเขาทำให้โค้ดของฉันสั้นลงเข้าใจง่ายขึ้นหรืออะไร?

อัปเดต

มีคนโพสต์คำตอบมากมาย แต่คงจะดีหากได้เห็นตัวอย่างของการมีและไม่มีตัวแปลงสัญญาณสำหรับบางสิ่งที่เรียบง่ายซึ่งแม้แต่คนงี่เง่าอย่างฉันก็สามารถเข้าใจได้ เว้นแต่แน่นอนว่าทรานสดิวเซอร์ต้องการความเข้าใจในระดับสูงซึ่งในกรณีนี้ฉันจะไม่มีวันเข้าใจพวกเขา :(

คำตอบ:


75

ทรานสดิวเซอร์เป็นสูตรที่จะทำอย่างไรกับลำดับข้อมูลโดยที่ไม่รู้ว่าลำดับพื้นฐานคืออะไร (ทำอย่างไร) อาจเป็นช่อง seq, async หรืออาจสังเกตได้

พวกเขาเป็นแบบประกอบและหลายรูปแบบ

ข้อดีคือคุณไม่จำเป็นต้องใช้ตัวรวมมาตรฐานทั้งหมดทุกครั้งที่มีการเพิ่มแหล่งข้อมูลใหม่ ครั้งแล้วครั้งเล่า. ผลที่ตามมาคือคุณในขณะที่ผู้ใช้สามารถนำสูตรอาหารเหล่านั้นกลับมาใช้ซ้ำในแหล่งข้อมูลต่างๆได้

อัปเดตโฆษณา

เวอร์ชันก่อนหน้า 1.7 ของ Clojure คุณมีสามวิธีในการเขียนแบบสอบถามกระแสข้อมูล:

  1. การโทรที่ซ้อนกัน
    (reduce + (filter odd? (map #(+ 2 %) (range 0 10))))
  1. องค์ประกอบการทำงาน
    (def xform
      (comp
        (partial filter odd?)
        (partial map #(+ 2 %))))
    (reduce + (xform (range 0 10)))
  1. มาโครเธรด
    (defn xform [xs]
      (->> xs
           (map #(+ 2 %))
           (filter odd?)))
    (reduce + (xform (range 0 10)))

ด้วยทรานสดิวเซอร์คุณจะเขียนมันดังนี้:

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

พวกเขาทั้งหมดทำเหมือนกัน ความแตกต่างคือคุณไม่เคยโทรหา Transducers โดยตรงคุณส่งต่อไปยังฟังก์ชันอื่น ทรานสดิวเซอร์รู้ว่าต้องทำอย่างไรฟังก์ชันที่รับทรานสดิวเซอร์รู้ได้อย่างไร ลำดับของตัวรวมกันก็เหมือนกับที่คุณเขียนด้วยมาโครเธรด (ลำดับธรรมชาติ) ตอนนี้คุณสามารถใช้ซ้ำxformกับช่อง:

(chan 1 xform)

3
ฉันยิ่งมองหาคำตอบซึ่งมาพร้อมกับตัวอย่างที่แสดงให้ฉันเห็นว่าทรานสดิวเซอร์ช่วยฉันประหยัดเวลาได้อย่างไร
appshare.co

พวกเขาไม่ได้ถ้าคุณไม่ใช่ Clojure หรือผู้ดูแลระบบ dataflow lib
AlešRoubíček

5
ไม่ใช่การตัดสินใจทางเทคนิค เราใช้การตัดสินใจตามมูลค่าทางธุรกิจเท่านั้น "แค่ใช้" จะทำให้ฉันถูกไล่ออก
appshare.co

1
คุณอาจมีเวลารักษางานได้ง่ายขึ้นหากคุณพยายามใช้ทรานสดิวเซอร์ล่าช้าจนกว่า Clojure 1.7 จะถูกปลด
user100464

7
ทรานสดิวเซอร์ดูเหมือนจะเป็นวิธีที่มีประโยชน์ในการสรุปรูปแบบต่างๆของวัตถุที่ทำซ้ำได้ สิ่งเหล่านี้สามารถไม่สิ้นเปลืองเช่น Clojure seqs หรือวัสดุสิ้นเปลือง (เช่นช่อง async) ในแง่นี้สำหรับฉันแล้วดูเหมือนว่าคุณจะได้รับประโยชน์อย่างมากจากการใช้ทรานสดิวเซอร์หากเช่นคุณเปลี่ยนจากการใช้งานแบบ seq เป็น core.async การใช้งานโดยใช้ช่องทาง ทรานสดิวเซอร์ควรช่วยให้คุณรักษาหลักของตรรกะของคุณไว้ไม่เปลี่ยนแปลง การใช้การประมวลผลตามลำดับแบบดั้งเดิมคุณจะต้องแปลงสิ่งนี้เพื่อใช้ทรานสดิวเซอร์หรืออะนาล็อกคอร์ - async บางตัว นั่นเป็นกรณีทางธุรกิจ
Nathan Davis

47

ทรานสดิวเซอร์ปรับปรุงประสิทธิภาพและช่วยให้คุณเขียนโค้ดที่มีประสิทธิภาพด้วยวิธีโมดูลาร์มากขึ้น

นี่คือการวิ่งผ่านที่ดี

เมื่อเทียบกับการแต่งสายเก่าmap, filter, reduceฯลฯ คุณจะได้รับประสิทธิภาพที่ดีขึ้นเพราะคุณไม่จำเป็นต้องสร้างคอลเลกชันกลางระหว่างแต่ละขั้นตอนและซ้ำ ๆ เดินคอลเลกชันเหล่านั้น

เมื่อเทียบกับreducersหรือเขียนการดำเนินการทั้งหมดของคุณให้เป็นนิพจน์เดียวด้วยตนเองคุณจะสามารถใช้นามธรรมได้ง่ายขึ้นความเป็นโมดูลาร์ที่ดีขึ้นและการใช้ฟังก์ชันการประมวลผลซ้ำ


2
เพียงแค่อยากรู้คุณได้กล่าวไว้ข้างต้น: "การสร้างคอลเลกชันระดับกลางระหว่างแต่ละขั้นตอน" แต่ "คอลเลกชันระดับกลาง" ฟังดูเหมือนต่อต้านรูปแบบไม่ใช่หรือ .NET นำเสนอการแจกแจงแบบขี้เกียจ Java มีสตรีมที่ขี้เกียจหรือการเล่นซ้ำที่ขับเคลื่อนด้วย Guava Haskell ที่ขี้เกียจก็ต้องมีบางอย่างที่ขี้เกียจเช่นกัน สิ่งเหล่านี้ไม่จำเป็นต้องใช้map/ reduceใช้คอลเลกชันระดับกลางเนื่องจากทั้งหมดสร้างห่วงโซ่ตัววนซ้ำ ที่นี่ฉันผิดตรงไหน?
Lyubomyr Shaydariv

3
ปิดบังmapและfilterสร้างคอลเลกชันระดับกลางเมื่อซ้อนกัน
noisesmith

4
และอย่างน้อยก็เกี่ยวกับความขี้เกียจของ Clojure ปัญหาของความเกียจคร้านนั้นมีมุมฉากอยู่ที่นี่ ใช่แมปและตัวกรองขี้เกียจนอกจากนี้ยังสร้างคอนเทนเนอร์สำหรับค่าขี้เกียจเมื่อคุณเชื่อมโยง หากคุณไม่ยึดหัวนอนคุณจะไม่สร้างลำดับขี้เกียจขนาดใหญ่ที่ไม่จำเป็น แต่คุณยังคงสร้างนามธรรมระดับกลางสำหรับองค์ประกอบขี้เกียจแต่ละองค์ประกอบ
noisesmith

ตัวอย่าง woud be nice.
appshare.co

8
@LyubomyrShaydariv โดย "คอลเลกชันระดับกลาง" noisesmith ไม่ได้หมายความว่า "วนซ้ำ / reify คอลเลกชันทั้งหมดจากนั้นวนซ้ำ / reify อีกคอลเลกชันทั้งหมด" เขาหรือเธอหมายความว่าเมื่อคุณซ้อนฟังก์ชันการเรียกที่ส่งคืนลำดับการเรียกใช้ฟังก์ชันแต่ละครั้งจะทำให้เกิดการสร้างลำดับใหม่ การทำซ้ำจริงยังคงเกิดขึ้นเพียงครั้งเดียว แต่มีการใช้หน่วยความจำเพิ่มเติมและการจัดสรรอ็อบเจ็กต์เนื่องจากลำดับที่ซ้อนกัน
erikprice

22

ทรานสดิวเซอร์เป็นวิธีการรวมกันเพื่อลดฟังก์ชัน

ตัวอย่าง: ฟังก์ชันการลดคือฟังก์ชันที่รับอาร์กิวเมนต์สองค่า: ผลลัพธ์จนถึงตอนนี้และอินพุต พวกเขาส่งคืนผลลัพธ์ใหม่ (จนถึงปัจจุบัน) ตัวอย่างเช่น+เมื่อมีอาร์กิวเมนต์สองตัวคุณสามารถคิดว่าอันแรกเป็นผลลัพธ์จนถึงตอนนี้และอันที่สองเป็นอินพุต

ตอนนี้ทรานสดิวเซอร์สามารถใช้ฟังก์ชัน + และทำให้เป็นฟังก์ชันบวกสองครั้ง (เพิ่มทุกอินพุตเป็นสองเท่าก่อนเพิ่ม) นี่คือลักษณะของตัวแปลงสัญญาณ (ในแง่พื้นฐานส่วนใหญ่):

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

แทนภาพประกอบrfnด้วย+เพื่อดูว่า+จะเปลี่ยนเป็นครั้งที่สองบวก:

(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

ดังนั้น

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

ตอนนี้จะให้ผล 12

การลดฟังก์ชันที่ส่งคืนโดยทรานสดิวเซอร์ไม่ขึ้นอยู่กับวิธีการสะสมของผลลัพธ์เนื่องจากฟังก์ชันเหล่านี้สะสมด้วยฟังก์ชันการลดที่ส่งผ่านไปยังฟังก์ชันเหล่านั้นโดยไม่รู้วิธี ที่นี่เราใช้conjแทน+. Conjรับคอลเล็กชันและค่าและส่งคืนคอลเล็กชันใหม่พร้อมกับค่านั้นต่อท้าย

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

จะให้ผล [2 4 6]

นอกจากนี้ยังไม่ขึ้นกับแหล่งที่มาของอินพุต

ทรานสดิวเซอร์หลายตัวสามารถผูกมัดเป็นสูตร (chainable) เพื่อเปลี่ยนฟังก์ชันการลด

อัปเดต: เนื่องจากตอนนี้มีหน้าอย่างเป็นทางการเกี่ยวกับเรื่องนี้ฉันขอแนะนำให้อ่าน: http://clojure.org/transducers


คำอธิบายที่ดี แต่ในไม่ช้าก็มีศัพท์แสงมากเกินไปสำหรับฉัน "การลดฟังก์ชันที่สร้างโดยทรานสดิวเซอร์ไม่ขึ้นอยู่กับวิธีสะสมของผลลัพธ์"
appshare.co

1
คุณพูดถูกคำที่สร้างขึ้นไม่เหมาะสมที่นี่
Leon Grapenthin

ไม่เป็นไร. อย่างไรก็ตามฉันเข้าใจว่า Transformers เป็นเพียงการเพิ่มประสิทธิภาพในขณะนี้ดังนั้นจึงไม่ควรใช้ต่อไป
appshare.co

1
เป็นวิธีการรวมกันสำหรับการลดฟังก์ชัน มีที่ไหนอีกบ้าง นี่เป็นมากกว่าการเพิ่มประสิทธิภาพ
Leon Grapenthin

ฉันพบว่าคำตอบนี้น่าสนใจมาก แต่ก็ไม่ชัดเจนสำหรับฉันว่ามันเชื่อมต่อกับทรานสดิวเซอร์อย่างไร (ส่วนหนึ่งเป็นเพราะฉันยังพบว่าหัวเรื่องสับสน) ความสัมพันธ์ระหว่างdoubleและtransduceคืออะไร?
อังคาร

22

สมมติว่าคุณต้องการใช้ชุดฟังก์ชันเพื่อเปลี่ยนสตรีมข้อมูล เปลือก Unix ช่วยให้คุณทำสิ่งนี้กับตัวดำเนินการท่อเช่น

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

(คำสั่งด้านบนจะนับจำนวนผู้ใช้ที่มีตัวอักษร r เป็นตัวพิมพ์ใหญ่หรือตัวพิมพ์เล็กในชื่อผู้ใช้) สิ่งนี้ถูกนำไปใช้เป็นชุดของกระบวนการซึ่งแต่ละกระบวนการอ่านจากผลลัพธ์ของกระบวนการก่อนหน้าดังนั้นจึงมีสตรีมกลางสี่สาย คุณสามารถจินตนาการถึงการนำไปใช้งานที่แตกต่างกันซึ่งประกอบด้วยคำสั่งห้าคำสั่งเป็นคำสั่งรวมคำสั่งเดียวซึ่งจะอ่านจากอินพุตและเขียนเอาต์พุตทุกครั้ง หากสตรีมระดับกลางมีราคาแพงและองค์ประกอบราคาถูกนั่นอาจเป็นการแลกเปลี่ยนที่ดี

สิ่งเดียวกันนี้ถือสำหรับ Clojure มีหลายวิธีในการแสดงไปป์ไลน์ของการเปลี่ยนแปลง แต่ขึ้นอยู่กับว่าคุณทำอย่างไรคุณสามารถจบลงด้วยสตรีมระดับกลางที่ส่งผ่านจากฟังก์ชันหนึ่งไปยังฟังก์ชันถัดไป หากคุณมีข้อมูลจำนวนมากการรวบรวมฟังก์ชันเหล่านั้นให้เป็นฟังก์ชันเดียวจะเร็วกว่า ทรานสดิวเซอร์ทำให้ง่ายต่อการทำเช่นนั้น นวัตกรรม Clojure รุ่นก่อนหน้านี้ช่วยให้คุณทำเช่นนั้นได้เช่นกัน แต่มีข้อ จำกัด บางประการ ทรานสดิวเซอร์ลบข้อ จำกัด บางส่วนออกไป

ดังนั้นเพื่อตอบคำถามของคุณทรานสดิวเซอร์ไม่จำเป็นต้องทำให้โค้ดของคุณสั้นลงหรือเข้าใจได้มากขึ้น แต่โค้ดของคุณอาจจะไม่เข้าใจนานขึ้นหรือน้อยลงเช่นกันและหากคุณกำลังทำงานกับข้อมูลจำนวนมากตัวแปลงสัญญาณสามารถสร้างโค้ดของคุณได้ เร็วขึ้น

นี่เป็นภาพรวมของทรานสดิวเซอร์ที่ค่อนข้างดี


1
อ๊ะทรานสดิวเซอร์ส่วนใหญ่เป็นการเพิ่มประสิทธิภาพการทำงานนั่นคือสิ่งที่คุณพูดหรือไม่?
appshare.co

@Zubair ใช่ถูกต้อง โปรดทราบว่าการเพิ่มประสิทธิภาพนั้นนอกเหนือไปจากการกำจัดสตรีมกลาง คุณยังสามารถดำเนินการควบคู่กันได้
user100464

2
เป็นมูลค่าการกล่าวขวัญpmapซึ่งดูเหมือนจะไม่ได้รับความสนใจมากพอ หากคุณกำลังmapping ฟังก์ชันที่มีราคาแพงในลำดับการดำเนินการแบบขนานนั้นทำได้ง่ายเพียงแค่เพิ่ม "p" ไม่จำเป็นต้องเปลี่ยนแปลงสิ่งอื่นใดในโค้ดของคุณและพร้อมให้บริการแล้วไม่ใช่อัลฟ่าไม่ใช่เบต้า (ถ้าฟังก์ชั่นสร้างลำดับกลางตัวแปลงสัญญาณอาจเร็วกว่านี้ฉันจะเดา)
อังคาร

10

Rich Hickey บรรยาย 'Transducers' ในการประชุม Strange Loop 2014 (45 นาที)

เขาอธิบายง่ายๆว่าทรานสดิวเซอร์คืออะไรพร้อมตัวอย่างในโลกแห่งความเป็นจริง - การแปรรูปกระเป๋าในสนามบิน เขาแยกแง่มุมที่แตกต่างกันอย่างชัดเจนและแตกต่างกับแนวทางปัจจุบัน ในตอนท้ายเขาให้เหตุผลสำหรับการดำรงอยู่ของพวกเขา

วิดีโอ: https://www.youtube.com/watch?v=6mTbuzafcII


8

ฉันพบว่าการอ่านตัวอย่างจากทรานสดิวเซอร์ -Jsช่วยให้ฉันเข้าใจในแง่ที่เป็นรูปธรรมว่าฉันจะใช้มันในโค้ดวันต่อวันได้อย่างไร

ตัวอย่างเช่นพิจารณาตัวอย่างนี้ (นำมาจาก README ที่ลิงค์ด้านบน):

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]

ประการแรกการใช้งานxfจะดูสะอาดกว่าทางเลือกปกติด้วยเครื่องหมายขีดล่าง

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

ทำไมตัวอย่างทรานสดิวเซอร์จึงยาวขึ้นมาก เวอร์ชันขีดล่างดูกระชับขึ้นมาก
appshare.co

1
@Zubair ไม่จริงt.into([], t.comp(t.map(inc), t.filter(isEven)), [0,1,2,3,4])
Juan Castañeda

7

ทรานสดิวเซอร์เป็นฟังก์ชัน (ตามความเข้าใจของฉัน!) ซึ่งใช้ฟังก์ชันลดหนึ่งฟังก์ชันและส่งกลับอีกฟังก์ชันหนึ่ง ฟังก์ชันการลดคือฟังก์ชันหนึ่ง

ตัวอย่างเช่น:

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

ในกรณีนี้ตัวแปลงสัญญาณของฉันใช้ฟังก์ชันการกรองอินพุตซึ่งใช้กับ 0 แล้วถ้าค่านั้นเท่ากัน? ในกรณีแรกตัวกรองส่งค่านั้นไปยังตัวนับจากนั้นตัวกรองจะกรองค่าถัดไป แทนที่จะกรองก่อนแล้วส่งผ่านค่าเหล่านั้นไปนับ

มันเป็นสิ่งเดียวกันในตัวอย่างที่สองที่ตรวจสอบทีละค่าและถ้าค่านั้นน้อยกว่า 3 ก็ให้นับเพิ่ม 1


ฉันชอบคำอธิบายง่ายๆนี้
Ignacio

7

คำจำกัดความที่ชัดเจนของตัวแปลงสัญญาณอยู่ที่นี่:

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.

เพื่อทำความเข้าใจลองพิจารณาตัวอย่างง่ายๆต่อไปนี้:

;; 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}])

แล้วเราอยากรู้ว่ามีเด็กกี่คนในหมู่บ้าน? เราสามารถค้นหาได้อย่างง่ายดายด้วยตัวลดต่อไปนี้:

;; 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

นี่เป็นอีกวิธีหนึ่งที่ทำได้:

;; 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

นอกจากนี้ยังมีประสิทธิภาพมากเมื่อใช้กลุ่มย่อยในบัญชีด้วย ตัวอย่างเช่นหากเราต้องการทราบจำนวนเด็กในครอบครัวบราวน์เราสามารถดำเนินการ:

;; 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

ฉันหวังว่าคุณจะพบตัวอย่างเหล่านี้ที่เป็นประโยชน์ คุณสามารถค้นหาเพิ่มเติมได้ที่นี่

หวังว่าจะช่วยได้

Clemencio Morales Lucas


3
"ทรานสดิวเซอร์เป็นวิธีที่ทรงพลังและประกอบได้ในการสร้างการแปลงอัลกอริทึมที่คุณสามารถนำมาใช้ซ้ำได้ในหลาย ๆ บริบทและกำลังจะมาถึง Clojure core และ core.async" คำจำกัดความสามารถใช้ได้กับเกือบทุกอย่าง?
appshare.co

1
สำหรับ Clojure Transducer เกือบทุกชนิดฉันจะบอกว่า
Clemencio Morales Lucas

6
เป็นพันธกิจมากกว่าคำจำกัดความ
อังคาร

4

ฉันเขียนบล็อกเกี่ยวกับเรื่องนี้ด้วยตัวอย่าง clojurescript ซึ่งอธิบายว่าตอนนี้ฟังก์ชั่นลำดับสามารถขยายได้อย่างไรโดยสามารถแทนที่ฟังก์ชันการลด

นี่คือประเด็นของทรานสดิวเซอร์ที่ฉันอ่าน หากคุณคิดว่าเกี่ยวกับconsหรือconjการดำเนินการที่เขียนยากในการดำเนินงานเช่นmap, filterฯลฯ ฟังก์ชั่นการลดก็ไม่สามารถเข้าถึงได้

ด้วยทรานสดิวเซอร์ฟังก์ชันการลดจะถูกแยกออกและฉันสามารถแทนที่ได้เหมือนกับที่ฉันทำกับอาร์เรย์จาวาสคริปต์ดั้งเดิมpushด้วยตัวแปลงสัญญาณ

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

filter และเพื่อน ๆ มีการดำเนินการ 1 arity ใหม่ที่จะส่งคืนฟังก์ชันการแปลงสัญญาณที่คุณสามารถใช้เพื่อจัดหาฟังก์ชันการลดของคุณเอง


4

นี่คือศัพท์แสง (ส่วนใหญ่) และคำตอบที่ไม่มีรหัสของฉัน

ลองนึกถึงข้อมูลในสองวิธีสตรีม (ค่าที่เกิดขึ้นเมื่อเวลาผ่านไปเช่นเหตุการณ์) หรือโครงสร้าง (ข้อมูลที่มีอยู่ ณ ช่วงเวลาหนึ่งเช่นรายการเวกเตอร์อาร์เรย์ ฯลฯ )

มีการดำเนินการบางอย่างที่คุณอาจต้องการดำเนินการกับสตรีมหรือโครงสร้าง การดำเนินการอย่างหนึ่งคือการทำแผนที่ ฟังก์ชันการทำแผนที่อาจเพิ่มรายการข้อมูลแต่ละรายการ (สมมติว่าเป็นตัวเลข) ด้วย 1 และคุณคงพอจะนึกออกว่าสิ่งนี้จะนำไปใช้กับสตรีมหรือโครงสร้างได้อย่างไร

ฟังก์ชันการทำแผนที่เป็นเพียงคลาสหนึ่งของฟังก์ชันที่บางครั้งเรียกว่า "ฟังก์ชันการลด" ฟังก์ชันการลดทั่วไปอีกอย่างหนึ่งคือตัวกรองซึ่งจะลบค่าที่ตรงกับเพรดิเคต (เช่นลบค่าทั้งหมดที่เป็นคู่)

ทรานสดิวเซอร์ช่วยให้คุณ "ตัด" ลำดับของฟังก์ชันการลดอย่างน้อยหนึ่งฟังก์ชันและสร้าง "แพ็กเกจ" (ซึ่งก็คือฟังก์ชัน) ที่ทำงานได้ทั้งบนสตรีมหรือโครงสร้าง ตัวอย่างเช่นคุณสามารถ "แพ็กเกจ" ลำดับของฟังก์ชันการลด (เช่นกรองเลขคู่จากนั้นแมปตัวเลขผลลัพธ์เพื่อเพิ่มขึ้นทีละ 1) จากนั้นใช้ "แพ็กเกจ" ตัวแปลงสัญญาณนั้นกับสตรีมหรือโครงสร้างของค่า (หรือทั้งสองอย่าง) .

มีอะไรพิเศษเกี่ยวกับเรื่องนี้? โดยปกติแล้วฟังก์ชันการลดขนาดไม่สามารถประกอบได้อย่างมีประสิทธิภาพเพื่อทำงานทั้งบนสตรีมและโครงสร้าง

ประโยชน์ที่คุณจะได้รับคือคุณสามารถใช้ประโยชน์จากความรู้เกี่ยวกับฟังก์ชันเหล่านี้และนำไปใช้กับกรณีการใช้งานได้มากขึ้น ค่าใช้จ่ายสำหรับคุณคือคุณต้องเรียนรู้เครื่องจักรพิเศษบางอย่าง (เช่นตัวแปลงสัญญาณ) เพื่อให้คุณได้รับพลังพิเศษนี้


2

เท่าที่ฉันเข้าใจพวกเขาเป็นเหมือนหน่วยการสร้างโดยแยกออกจากการใช้อินพุตและเอาต์พุต คุณเพียงแค่กำหนดการดำเนินการ

เนื่องจากการใช้งานการดำเนินการไม่ได้อยู่ในรหัสของอินพุตและไม่มีการดำเนินการใด ๆ กับเอาต์พุตตัวแปลงสัญญาณจึงสามารถใช้ซ้ำได้อย่างมาก พวกเขาเตือนฉันของการไหลในAkka Streams

ฉันยังใหม่กับทรานสดิวเซอร์ขออภัยสำหรับคำตอบที่อาจไม่ชัดเจน


1

ฉันพบว่าโพสต์นี้ให้มุมมองของทรานสดิวเซอร์แบบเบิร์ดอายมากขึ้น

https://medium.com/@roman01la/understand-transducers-in-javascript-3500d3bd9624


3
คำตอบที่อาศัยเพียงลิงก์ภายนอกไม่ได้รับการสนับสนุนบน SO เนื่องจากลิงก์อาจพังได้ตลอดเวลาในอนาคต อ้างเนื้อหาในคำตอบของคุณแทน
Vincent Cantin

@VincentCantin ในความเป็นจริงโพสต์ขนาดกลางถูกลบไปแล้ว
Dmitri Zaitsev

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.