รายการร็อค
โครงสร้างข้อมูลที่เป็นมิตรมากที่สุดสำหรับข้อมูลตามลำดับใน Haskell คือรายการ
data [a] = a:[a] | []
รายการให้ cons (1) ข้อเสียและการจับคู่รูปแบบ ห้องสมุดมาตรฐานและที่สำคัญโหมโรง, เต็มไปด้วยฟังก์ชั่นรายการที่มีประโยชน์ที่ควรครอกรหัสของคุณ ( foldr
, map
, filter
) รายการมีความคงทนถาวรหรือใช้งานได้ดีซึ่งดีมาก รายการ Haskell ไม่ใช่ "รายการ" จริง ๆ เพราะเป็นแบบเหรียญ (ภาษาอื่นเรียกว่าสตรีมเหล่านี้) ดังนั้นสิ่งที่ต้องการ
ones :: [Integer]
ones = 1:ones
twos = map (+1) ones
tenTwos = take 10 twos
ทำงานอย่างยอดเยี่ยม โครงสร้างข้อมูลที่ไม่มีที่สิ้นสุดร็อค
รายการใน Haskell ให้ส่วนต่อประสานเหมือนกับตัววนซ้ำในภาษาที่จำเป็น (เพราะความเกียจคร้าน) ดังนั้นจึงสมเหตุสมผลที่ใช้กันอย่างแพร่หลาย
ในทางกลับกัน
ปัญหาแรกของรายการคือการทำดัชนีให้(!!)
ใช้เวลา ϴ (k) ซึ่งน่ารำคาญ ยิ่งไปกว่านั้นการผนวกอาจช้า++
แต่โมเดลการประเมินผลที่ขี้เกียจของ Haskell หมายความว่าสิ่งเหล่านี้สามารถได้รับการตัดจำหน่ายอย่างสมบูรณ์หากเกิดขึ้น
ปัญหาที่สองกับรายการคือพวกเขามีตำแหน่งข้อมูลไม่ดี ตัวประมวลผลที่แท้จริงจะมีค่าคงที่สูงเมื่อวัตถุในหน่วยความจำไม่ถูกจัดวางติดกัน ดังนั้นใน C ++ std::vector
จะมี "snoc" ที่เร็วกว่า (วางวัตถุในตอนท้าย) กว่าโครงสร้างข้อมูลลิสต์ลิงก์ที่ฉันรู้จักแม้ว่านี่จะไม่ใช่โครงสร้างข้อมูลแบบถาวรดังนั้นจึงเป็นมิตรน้อยกว่าลิสต์ของ Haskell
ปัญหาที่สามกับรายการคือพวกเขามีประสิทธิภาพพื้นที่ที่ไม่ดี พอยน์เตอร์พิเศษจำนวนหนึ่งช่วยเพิ่มพื้นที่เก็บข้อมูลของคุณ (โดยปัจจัยคงที่)
ลำดับมีการทำงาน
Data.Sequence
ขึ้นอยู่กับต้นไม้ภายใน (ฉันรู้ว่าคุณไม่ต้องการที่จะรู้สิ่งนี้) ซึ่งหมายความว่าพวกเขามีคุณสมบัติที่ดีบางอย่าง
- ทำงานได้อย่างหมดจด
Data.Sequence
เป็นโครงสร้างข้อมูลถาวร
- เข้าถึงอย่างรวดเร็วไปยังจุดเริ่มต้นและจุดสิ้นสุดของต้นไม้ ϴ (1) (ตัดจำหน่าย) เพื่อรับองค์ประกอบแรกหรือสุดท้ายหรือต่อท้ายต้นไม้ ที่รายการสิ่งที่เร็วที่สุด
Data.Sequence
คืออย่างช้าที่สุดคงที่
- access (บันทึก n) เข้าถึงตรงกลางของลำดับ ซึ่งรวมถึงการแทรกค่าเพื่อสร้างลำดับใหม่
- API คุณภาพสูง
ในทางกลับกันData.Sequence
ไม่ได้ทำอะไรมากสำหรับปัญหาในพื้นที่ข้อมูลและใช้งานได้กับการรวบรวมแบบ จำกัด เท่านั้น (ขี้เกียจน้อยกว่ารายการ)
อาร์เรย์ไม่เหมาะสำหรับคนที่ใจหดหู่
อาร์เรย์เป็นหนึ่งในโครงสร้างข้อมูลที่สำคัญที่สุดใน CS แต่มันไม่เหมาะกับโลกการทำงานอันแสนขี้เกียจ อาร์เรย์ให้การเข้าถึง middle (1) ตรงกลางของการรวบรวมและตำแหน่งข้อมูล / ปัจจัยคงที่ที่ดีเป็นพิเศษ แต่เนื่องจากพวกเขาไม่เหมาะกับ Haskell มากพวกเขาจึงเจ็บปวดที่จะใช้ จริงๆแล้วมีประเภทอาร์เรย์ที่แตกต่างกันจำนวนมากในไลบรารีมาตรฐานปัจจุบัน สิ่งเหล่านี้รวมถึงอาร์เรย์ที่คงอยู่อย่างถาวร, อาร์เรย์ที่ไม่แน่นอนสำหรับ IO monad, อาร์เรย์ที่ไม่แน่นอนสำหรับ ST monad และรุ่นที่ไม่ได้บรรจุข้างต้น ดูรายละเอียดเพิ่มเติมได้ที่ haskell wiki
เวกเตอร์เป็นอาร์เรย์ที่ "ดีกว่า"
Data.Vector
แพคเกจให้บริการทั้งหมดของความดีอาร์เรย์ในระดับสูงและการทำความสะอาด API ที่สูงขึ้น หากคุณไม่ทราบว่ากำลังทำอะไรอยู่คุณควรใช้สิ่งเหล่านี้หากคุณต้องการอาเรย์เช่นประสิทธิภาพ แน่นอนยังมีข้อแม้บางประการ - อาเรย์ที่เปลี่ยนแปลงไม่ได้เช่นโครงสร้างข้อมูลไม่เล่นดีในภาษาขี้เกียจ อย่างไรก็ตามบางครั้งคุณต้องการประสิทธิภาพ O (1) และ Data.Vector
มอบให้คุณในแพ็คเกจที่ใช้งานได้
คุณมีตัวเลือกอื่น ๆ
หากคุณต้องการรายการที่มีความสามารถในการแทรกอย่างมีประสิทธิภาพในตอนท้ายคุณสามารถใช้รายการที่แตกต่างได้ ตัวอย่างที่ดีที่สุดของรายการกวดขันขึ้นประสิทธิภาพการทำงานมีแนวโน้มที่จะมาจากการ[Char]
ที่โหมโรงได้ aliased String
เป็น Char
รายการมีความสะดวก แต่มีแนวโน้มที่จะรันตามลำดับช้ากว่าสตริง C ถึง 20 เท่าดังนั้นอย่าลังเลที่จะใช้งานData.Text
หรือเร็วมาก Data.ByteString
ฉันแน่ใจว่ามีห้องสมุดที่มุ่งเน้นลำดับอื่น ๆ ที่ฉันไม่ได้คิดตอนนี้
ข้อสรุป
90 +% ของเวลาที่ฉันต้องการการรวบรวมตามลำดับในรายการ Haskell เป็นโครงสร้างข้อมูลที่ถูกต้อง รายการนั้นเหมือนตัววนซ้ำฟังก์ชันที่ใช้รายการสามารถใช้กับโครงสร้างข้อมูลอื่น ๆ เหล่านี้ได้อย่างง่ายดายโดยใช้toList
ฟังก์ชันที่มาพร้อมกับ ในโลกที่ดีกว่าการโหมโรงจะเป็นตัวแปรอย่างสมบูรณ์เกี่ยวกับประเภทของคอนเทนเนอร์ที่ใช้ แต่ในปัจจุบัน []
litters ห้องสมุดมาตรฐาน ดังนั้นการใช้รายการ (เกือบ) ทุกที่ไม่เป็นไรแน่นอน
คุณสามารถรับฟังก์ชั่นรายการส่วนใหญ่ (และมีเกียรติที่จะใช้พวกเขา)
Prelude.map ---> Prelude.fmap (works for every Functor)
Prelude.foldr/foldl/etc ---> Data.Foldable.foldr/foldl/etc
Prelude.sequence ---> Data.Traversable.sequence
etc
ในความเป็นจริงData.Traversable
กำหนด API ที่เป็นสากลมากขึ้นหรือน้อยลงในรายการ "สิ่งใด ๆ "
ถึงแม้ว่าคุณสามารถทำได้ดีและเขียนรหัสพารามิเตอร์เพียงอย่างเดียว แต่พวกเราส่วนใหญ่ไม่ได้ใช้รายการทั่วทุกที่ หากคุณกำลังเรียนรู้ฉันขอแนะนำให้คุณทำเช่นกัน
แก้ไข: ขึ้นอยู่กับความเห็นของฉันรู้ฉันไม่เคยอธิบายเมื่อจะใช้VSData.Vector
Data.Sequence
อาร์เรย์และเวกเตอร์ให้การจัดทำดัชนีและการแบ่งส่วนข้อมูลได้อย่างรวดเร็วที่สุด แต่มีโครงสร้างข้อมูลชั่วคราว (จำเป็น) พื้นฐาน โครงสร้างข้อมูลที่สามารถใช้งานได้จริงเช่นData.Sequence
และ[]
ปล่อยให้สร้างคุณค่าใหม่จากค่าเก่าราวกับว่าคุณได้แก้ไขค่าเก่า
newList oldList = 7 : drop 5 oldList
ไม่แก้ไขรายการเก่าและไม่ต้องคัดลอก ดังนั้นแม้ว่าoldList
จะยาวอย่างไม่น่าเชื่อ "การปรับเปลี่ยน" นี้จะเร็วมาก เหมือนกับ
newSequence newValue oldSequence = Sequence.update 3000 newValue oldSequence
จะสร้างลำดับใหม่โดยมีnewValue
การแทนที่สำหรับองค์ประกอบ 3000 รายการ อีกครั้งมันไม่ทำลายลำดับเก่า แต่เพิ่งสร้างใหม่ แต่การทำเช่นนี้มีประสิทธิภาพมากโดยใช้ O (log (min (k, kn)) โดยที่ n คือความยาวของลำดับและ k คือดัชนีที่คุณปรับเปลี่ยน
คุณลาดเทได้อย่างง่ายดายทำเช่นนี้กับและVectors
Arrays
พวกเขาสามารถแก้ไขได้แต่นั่นคือการแก้ไขที่จำเป็นจริงและไม่สามารถทำได้ในรหัส Haskell ปกติ นั่นหมายถึงการดำเนินการในVector
แพ็คเกจที่ทำการปรับเปลี่ยนเช่นsnoc
และcons
ต้องคัดลอกทั้งเวกเตอร์เพื่อใช้O(n)
เวลา ข้อยกเว้นเพียงอย่างเดียวคือคุณสามารถใช้เวอร์ชันที่ไม่แน่นอน ( Vector.Mutable
) ภายในST
monad (หรือIO
) และทำการแก้ไขทั้งหมดของคุณเช่นเดียวกับที่คุณต้องการในภาษาที่จำเป็น เมื่อเสร็จแล้วคุณ "หยุด" เวกเตอร์ของคุณเพื่อเปลี่ยนเป็นโครงสร้างที่ไม่เปลี่ยนรูปแบบที่คุณต้องการใช้กับรหัสบริสุทธิ์
ความรู้สึกของฉันคือคุณควรใช้ค่าเริ่มต้นData.Sequence
หากรายการไม่เหมาะสม ใช้Data.Vector
เฉพาะเมื่อรูปแบบการใช้งานของคุณไม่เกี่ยวข้องกับการปรับเปลี่ยนมากมายหรือถ้าคุณต้องการประสิทธิภาพที่สูงมากภายใน Monads ST / IO
ถ้าหากการพูดคุยของนี้ST
monad จะออกจากคุณสับสน: Data.Sequence
ทั้งหมดเป็นเหตุผลที่ต้องติดได้อย่างรวดเร็วบริสุทธิ์และสวยงาม