Clojure: ข้อเสีย (seq) กับ conj (รายการ)


100

ฉันรู้ว่าconsส่งคืน seq และconjส่งคืนคอลเล็กชัน ฉันรู้ด้วยว่าconj"เพิ่ม" รายการนั้นไว้ที่ส่วนท้ายที่เหมาะสมที่สุดของคอลเลคชันและcons"เพิ่ม" รายการไว้ด้านหน้าเสมอ ตัวอย่างนี้แสดงให้เห็นทั้งสองประเด็นเหล่านี้:

user=> (conj [1 2 3] 4) ; returns a collection
[1 2 3 4]
user=> (cons 4 [1 2 3]) ; returns a seq
(4 1 2 3)

สำหรับเวกเตอร์แผนที่และชุดความแตกต่างเหล่านี้มีความหมายสำหรับฉัน อย่างไรก็ตามสำหรับรายการนั้นดูเหมือนจะเหมือนกัน

user=> (conj (list 3 2 1) 4) ; returns a list
(4 3 2 1)
user=> (cons 4 (list 3 2 1)) ; returns a seq
(4 3 2 1)

มีตัวอย่างใด ๆ โดยใช้รายการที่conjเทียบกับconsการจัดแสดงพฤติกรรมที่แตกต่างกันหรือว่าพวกเขาใช้แทนกันได้อย่างแท้จริง? วลีต่างกันมีตัวอย่างที่ไม่สามารถใช้ list และ seq เทียบเท่ากันได้หรือไม่?

คำตอบ:


153

ความแตกต่างอย่างหนึ่งคือconjยอมรับอาร์กิวเมนต์จำนวนเท่าใดก็ได้เพื่อแทรกลงในคอลเล็กชันในขณะที่consใช้เพียงข้อเดียว:

(conj '(1 2 3) 4 5 6)
; => (6 5 4 1 2 3)

(cons 4 5 6 '(1 2 3))
; => IllegalArgumentException due to wrong arity

ความแตกต่างอีกประการหนึ่งอยู่ในระดับของค่าส่งคืน:

(class (conj '(1 2 3) 4))
; => clojure.lang.PersistentList

(class (cons 4 '(1 2 3))
; => clojure.lang.Cons

โปรดทราบว่าสิ่งเหล่านี้ใช้แทนกันไม่ได้จริงๆ โดยเฉพาะอย่างยิ่งclojure.lang.Consไม่ได้ใช้งานclojure.lang.Countedดังนั้นcountบนมันจึงไม่ใช่การดำเนินการเวลาคงที่อีกต่อไป (ในกรณีนี้อาจลดลงเหลือ 1 + 3 - 1 มาจากการเคลื่อนที่เชิงเส้นเหนือองค์ประกอบแรก 3 มาจาก(next (cons 4 '(1 2 3))การเป็นPersistentListและ ดังนั้นCounted)

ความตั้งใจที่อยู่เบื้องหลังชื่อคือฉันเชื่อว่านั่นconsหมายถึงข้อเสีย (truct a seq) 1ในขณะที่conjหมายถึงการ conj (oin รายการลงในคอลเลกชัน) seqถูกสร้างขึ้นโดยconsเริ่มต้นด้วยองค์ประกอบผ่านเป็นอาร์กิวเมนต์แรกและมีฐานะnext/ restส่วนสิ่งที่เกิดจากการประยุกต์ใช้seqเพื่ออาร์กิวเมนต์ที่สอง; clojure.lang.Consตามที่แสดงข้างต้นสิ่งทั้งหมดเป็นของชั้น ในทางตรงกันข้ามconjจะส่งคืนคอลเล็กชันประเภทใกล้เคียงกับคอลเล็กชันที่ส่งไปให้เสมอ (โดยประมาณเพราะ a PersistentArrayMapจะกลายPersistentHashMapเป็นทันทีที่มันเติบโตเกิน 9 รายการ)


1ตามเนื้อผ้าในโลกของเสียงกระเพื่อมconsข้อเสีย (tructs คู่) ดังนั้น Clojure ออกจากประเพณีชัดในการมีของฟังก์ชั่นการสร้างหมายเลขที่ไม่ได้มีแบบดั้งเดิมcons cdrการใช้งานโดยทั่วไปของconsto mean "สร้างเรกคอร์ดบางประเภทหรืออื่น ๆ เพื่อเก็บค่าต่างๆไว้ด้วยกัน" ในปัจจุบันมีการศึกษาภาษาโปรแกรมและการนำไปใช้อย่างแพร่หลาย นั่นคือความหมายเมื่อมีการกล่าวถึง "การหลีกเลี่ยงการยินยอม"


1
ช่างเป็นงานเขียนที่ยอดเยี่ยมมาก! ฉันไม่รู้ว่ามีประเภทจุดด้อย ทำได้ดี!
Daniel Yankowsky

ขอบคุณ. ยินดีที่ได้ทราบ :-)
Michał Marczyk

2
อนึ่งในกรณีพิเศษให้(cons foo nil)ส่งคืน singleton PersistentList(และเช่นเดียวกันสำหรับconj)
Michał Marczyk

1
อีกคำอธิบายที่ยอดเยี่ยม คุณเป็นเจได clojure อย่างแท้จริง!
dbyrne

1
จากประสบการณ์ของฉันการปฏิบัติต่อรายการเป็นรายการและไม่ใช่เรื่องที่สำคัญเมื่อประสิทธิภาพมีความสำคัญ
cgrand

11

ความเข้าใจของฉันคือสิ่งที่คุณพูดเป็นความจริง: conj ในรายการเทียบเท่ากับข้อเสียในรายการ

คุณสามารถคิดว่า conj เป็นการดำเนินการ "แทรกที่ใดที่หนึ่ง" และข้อเสียคือการดำเนินการ "แทรกที่ส่วนหัว" ในรายการมันเป็นตรรกะที่ดีที่สุดในการแทรกที่ส่วนหัวดังนั้น conj และข้อเสียจะเท่ากันในกรณีนี้


8

ความแตกต่างอีกประการหนึ่งก็คือเนื่องจากconjใช้ลำดับเป็นอาร์กิวเมนต์แรกจึงเล่นได้ดีalterเมื่ออัปเดต a เป็นrefลำดับบางส่วน:

(dosync (alter a-sequence-ref conj an-item))

โดยพื้นฐานแล้วสิ่งนี้ทำ(conj a-sequence-ref an-item)ในลักษณะที่ปลอดภัยต่อเธรด consนี้จะไม่ทำงานกับ ดูบทเกี่ยวกับภาวะพร้อมกันในการเขียนโปรแกรม Clojureโดย Stu Halloway สำหรับข้อมูลเพิ่มเติม


2

ความแตกต่างอีกประการหนึ่งคือพฤติกรรมของรายการ?

(list? (conj () 1)) ;=> true
(list? (cons 1 ())) ; => false

4
ข้อเสียจะส่งคืนลำดับที่ conj ส่งกลับประเภทเดียวกันกับที่ให้ไว้เสมอ
Ning Sun

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