วิธีการแปลงลำดับขี้เกียจเป็นไม่ขี้เกียจใน Clojure


95

ฉันลองทำสิ่งต่อไปนี้ใน Clojure โดยคาดหวังว่าจะมีการส่งคืนคลาสของลำดับที่ไม่ขี้เกียจ:

(.getClass (doall (take 3 (repeatedly rand))))

อย่างไรก็ตามสิ่งนี้ยังคงกลับclojure.lang.LazySeqมา ฉันเดาว่าdoallจะประเมินลำดับทั้งหมด แต่ส่งคืนลำดับเดิมเนื่องจากยังมีประโยชน์สำหรับการบันทึก

ดังนั้นวิธีการสร้างลำดับที่ไม่ขี้เกียจจากคนขี้เกียจคืออะไร?


ฉันแปลกใจที่ไม่มีใครถามว่าทำไมคุณถึงกังวลเกี่ยวกับประเภทที่แท้จริงของมูลค่าที่ส่งคืนของdoall
tar

คุณสามารถแปลงเป็นเวกเตอร์ได้:(vec (take 3 (repeatedly rand)))
Kris

คำตอบ:


162

doallคือทั้งหมดที่คุณต้องการ เพียงเพราะseqมีประเภทLazySeqไม่ได้หมายความว่ามีการประเมินที่รอดำเนินการ Lazy seqแคชผลลัพธ์ของพวกเขาดังนั้นสิ่งที่คุณต้องทำคือเดินขี้เกียจseqครั้งเดียว (ตามที่doallทำ) เพื่อบังคับมันทั้งหมดและทำให้มันไม่ขี้เกียจ seqไม่ได้บังคับให้ประเมินคอลเล็กชันทั้งหมด


2
ฉันเปลี่ยนเป็นคำตอบที่ยอมรับแล้ว ในบันทึกที่เกี่ยวข้องคุณจะทราบได้อย่างไรว่า LazySeq ได้รับการประเมินมาก่อนหน้านี้แล้วหรือไม่?
Tim Clemons

10
ฉันเชื่อว่าคุณแค่โทรrealized?.
toofarsideways

1
อาจมีควรจะมีการดำเนินการเพื่อให้ตรงกับrealize realized?
Reut Sharabani

ทั้งหมดนี้ดีมาก แต่เนื่องจากฟังก์ชั่นบางอย่างเช่นcontains?ไม่สนใจว่าคุณจะตระหนักถึง seq ที่ขี้เกียจหรือไม่สิ่งนี้จึงตอบคำถามเฉพาะตามที่ถาม แต่จะน้อยกว่าสำหรับชื่อของคำถาม
matanster

75

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

  • ลำดับขี้เกียจ ex-lazy (ประเมินโดยสมบูรณ์) (doall ... )
  • รายการสำหรับการเข้าถึงตามลำดับ (apply list (my-lazy-seq)) OR (into () ...)
  • เวกเตอร์สำหรับการเข้าถึงโดยสุ่มในภายหลัง (vec (my-lazy-seq))
  • แผนที่หรือชุดหากคุณมีจุดประสงค์พิเศษ

คุณสามารถมีลำดับประเภทใดก็ได้ที่เหมาะกับความต้องการของคุณมากที่สุด


นี่คือคำตอบที่ดีที่สุด
Felipe

4
คำตอบที่ยอมรับนั้นถูกต้องในทางเทคนิค แต่คำตอบนี้มีประโยชน์กับฉันมากที่สุด ฉันพยายามแมปฟังก์ชันบนเวกเตอร์แล้วคายผลลัพธ์ไปยังไฟล์และแม้กระทั่งหลังจากเรียก doall ไฟล์นั้นก็มี "clojure.lang.LazySeq@address" แทนเนื้อหาของลำดับ การโทรหา vec บนแผนที่ค่ากลับทำให้ฉันมีสิ่งที่จำเป็นในการคายไฟล์ออกมา
Jesse Rosalia

1
@JesseRosalia เป็นเรื่องดีที่รู้ว่าคำตอบของ Rich Hickey หนึ่งเดียวใน SO ทั้งหมดนั้นถูกต้องในทางเทคนิค ;-)
Phil Cooper

(vec (my-lazy-seq))ไม่ดีนักในสถานการณ์เช่นนี้: (vec (json/parse-string "{\"foo\":\"bar\"}")) ;; => [["foo" "bar"]]เนื่องจากcheshireเลือกที่จะผลิต lazy-seq จาก(json/parse-string)
codeasone

การบรรเทา(json/parse-string-strict)
อาการ

22

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

=> (realized? (take 3 (repeatedly rand))) 
false
=> (realized? (doall (take 3 (repeatedly rand)))) 
true

ประเภทที่แท้จริงไม่ได้เปลี่ยนแปลง แต่มีการสำนึก


2
มันเป็นมูลค่า noting แต่ที่คุณไม่จำเป็นที่จะบังคับให้ลำดับทั้งหมดสำหรับการกลับมาrealized? trueเช่น(let [r (range) r? (realized? r)] (doall (take 1 r)) [r? (realized? r)]) => [false true]
Alex Coventry

24
This Rich guy: D haha
Julian Krispel-Samsel

12
@nimrod :) แต่การเล่นสำนวนควรจะอยู่ใน "hís clojure"
ปีเตอร์

10
สำหรับผู้ที่ไม่รู้จัก "คนรวย" ได้คิดค้น Clojure
erturne

1
@AlexCovent ลองคืนตัวอย่างของคุณ[true true]

7

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

(use 'closure.walk)
(postwalk identity nested-lazy-thing)

ฉันพบว่าสิ่งนี้มีประโยชน์ในการทดสอบหน่วยที่ฉันต้องการบังคับให้ประเมินแอปพลิเคชันที่ซ้อนกันบางตัวmapเพื่อบังคับให้เกิดเงื่อนไขข้อผิดพลาด


5
(.getClass (into '() (take 3 (repeatedly rand))))

3
นี่เป็นความคิดที่ผิดมหันต์ มันย้อนกลับ seq อินพุต
amalloy

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