เหตุใดรายการข้อเสียเกี่ยวข้องกับฟังก์ชันการเขียนโปรแกรม


22

ฉันสังเกตเห็นว่าภาษาที่ใช้งานได้ส่วนใหญ่ใช้ลิสต์ที่เชื่อมโยงเดี่ยว ๆ (รายการ "ข้อเสีย") เป็นประเภทลิสต์พื้นฐานที่สุด ตัวอย่าง ได้แก่ Common LISP, Haskell และ F # สิ่งนี้แตกต่างจากภาษากระแสหลักโดยที่ประเภทรายการเนทีฟเป็นอาร์เรย์

ทำไมถึงเป็นอย่างนั้น?

สำหรับ Common LISP (ถูกพิมพ์แบบไดนามิก) ฉันเข้าใจว่าข้อเสียนั้นเป็นเรื่องทั่วไปมากพอที่จะเป็นฐานของรายการต้นไม้และอื่น ๆ นี่อาจเป็นเหตุผลเล็กน้อย

สำหรับภาษาที่พิมพ์แบบสแตติกฉันไม่สามารถหาเหตุผลที่ดีได้ฉันยังสามารถหาข้อโต้แย้งได้

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

เหตุใดจึงชอบรายการที่เชื่อมโยง


4
มาจากความคิดเห็นเกี่ยวกับคำตอบของ @ sepp2k ฉันคิดว่าan array is easier to share "partially" than a linked listต้องมีความกระจ่างในสิ่งที่คุณหมายถึง ตรงข้ามเป็นจริงตามที่ฉันเข้าใจ - คุณสามารถแชร์รายการที่เชื่อมโยงได้ง่ายขึ้นบางส่วนโดยผ่านโหนดใด ๆ ในนั้นในขณะที่อาร์เรย์จะต้องใช้เวลาในการทำสำเนาใหม่ หรือในแง่ของการแบ่งปันข้อมูลรายการที่เชื่อมโยงสองรายการสามารถชี้ไปที่คำต่อท้ายเดียวกันซึ่งไม่สามารถทำได้ด้วยอาร์เรย์
Izkata

หากอาร์เรย์กำหนดตัวเองเป็นชดเชยความยาวบัฟเฟอร์สามเท่าจากนั้นคุณสามารถแบ่งปันอาร์เรย์โดยการสร้างใหม่ที่มีชดเชย + 1, ความยาว -1 บัฟเฟอร์ หรือมีอาร์เรย์ชนิดพิเศษเป็น subarray
Dobes Vandermeer

@Izkata เมื่อพูดถึงอาร์เรย์เราไม่ค่อยหมายถึงบัฟเฟอร์เช่นตัวชี้ไปยังจุดเริ่มต้นของหน่วยความจำต่อเนื่องใน C เรามักจะหมายถึงโครงสร้างบางอย่างที่เก็บความยาวและตัวชี้ไปยังจุดเริ่มต้นของบัฟเฟอร์ที่ถูกห่อ ภายใต้ระบบดังกล่าวการดำเนินการแบ่งสามารถส่งคืน subarray ซึ่งตัวชี้บัฟเฟอร์ชี้ไปที่บัฟเฟอร์ (ที่องค์ประกอบแรกของอาร์เรย์ย่อย) และการนับที่เป็นเช่นนั้นนับเริ่มต้น + ให้องค์ประกอบสุดท้าย การดำเนินการแบ่งส่วนดังกล่าวเป็น O (1) ในเวลาและสถานที่
อเล็กซานเดอร์ - ไรน์สเตตโมนิก้า

คำตอบ:


22

ปัจจัยที่สำคัญที่สุดคือคุณสามารถเพิ่มรายการที่เชื่อมโยงไม่เปลี่ยนรูปแบบเดี่ยวในเวลา O (1) ซึ่งช่วยให้คุณสามารถสร้างรายการองค์ประกอบ n-recursively ในเวลา O (n) ดังนี้:

// Build a list containing the numbers 1 to n:
foo(0) = []
foo(n) = cons(n, foo(n-1))

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

รูปแบบการใช้งานช่วยกระตุ้นการใช้งานไม่ได้ดังนั้นยังใช้ข้อมูลร่วมกัน อาร์เรย์จะแบ่งปัน "บางส่วน" ได้ง่ายกว่ารายการที่ลิงก์

ฉันสมมติว่าการแชร์บางส่วนคุณหมายความว่าคุณสามารถใช้ subarray จากอาร์เรย์ในเวลา O (1) ในขณะที่รายการลิงก์ที่เชื่อมโยงคุณสามารถนำหางมาใช้ในเวลา O (1) และทุกอย่างอื่นต้องการ O (n) นั่นเป็นความจริง.

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


นั่นไม่จริงเลย คุณสามารถต่อท้ายอาร์เรย์ในค่าตัดจำหน่าย O (1)
DeadMG

10
@DeadMG ใช่ แต่ไม่ใช่อาร์เรย์ที่ไม่เปลี่ยนรูปแบบ
sepp2k

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

@Izkata OP พูดถึงบางส่วนของการแชร์อาร์เรย์ แต่ไม่ใช่รายการ นอกจากนี้ฉันไม่เคยได้ยินสิ่งที่คุณกำลังอธิบายเรียกว่าการแบ่งปันบางส่วน นั่นเป็นเพียงการแบ่งปัน
sepp2k

1
@Izkata OP ใช้คำว่า "array" สามครั้ง หนึ่งครั้งเพื่อกล่าวว่าภาษา FP ใช้รายการที่ลิงก์ซึ่งภาษาอื่นใช้อาร์เรย์ หนึ่งครั้งที่จะบอกว่าอาร์เรย์นั้นดีกว่าในการแบ่งปันบางส่วนดีกว่ารายการที่เชื่อมโยงและอีกครั้งหนึ่งที่จะบอกว่าอาร์เรย์นั้นสามารถจับคู่รูปแบบได้เช่นกัน (เช่นเดียวกับรายการที่ลิงก์) ในทุกกรณีเขาใช้ Array Array และรายการที่เชื่อมโยงกัน (เพื่อให้ประเด็นที่ Array นั้นมีประโยชน์มากกว่าเป็นโครงสร้างข้อมูลหลักมากกว่ารายการที่เชื่อมโยงนำไปสู่คำถามของเขาว่าทำไมรายการที่ถูกเชื่อมโยงจึงเป็นที่ต้องการใน FP) อาจใช้เงื่อนไขแทนกันได้
sepp2k

4

ฉันคิดว่ามันเป็นเรื่องของรายการที่ถูกนำไปใช้ในโค้ดที่ใช้งานได้ง่าย

โครงการ:

(define (cons x y)(lambda (m) (m x y)))

Haskell:

data  [a]  =  [] | a : [a]

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

นอกจากนี้การเรียกซ้ำจะทำงานได้ดีขึ้นในรายการมากกว่าอาร์เรย์ พิจารณาจำนวนครั้งที่คุณใช้ / สร้างรายการซ้ำและทำดัชนีอาร์เรย์ซ้ำ


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

@ sepp2k คุณหมายถึงอะไร"เก็บอะไร แต่ฟังก์ชั่น" ?
Pubby

1
สิ่งที่ฉันหมายถึงคือรายการที่กำหนดไว้อย่างนั้นไม่สามารถเก็บสิ่งที่ไม่ใช่ฟังก์ชันได้ แต่นั่นไม่จริงเลย Dunno ทำไมฉันถึงคิดอย่างนั้น ขอโทษสำหรับเรื่องนั้น.
sepp2k

2

รายการเดี่ยวที่เชื่อมโยงเป็นที่ง่ายโครงสร้างข้อมูลแบบถาวร

โครงสร้างข้อมูลถาวรเป็นสิ่งจำเป็นสำหรับการเขียนโปรแกรมที่มีประสิทธิภาพ


1
ดูเหมือนว่าจะทำซ้ำเพียงจุดที่ทำและอธิบายในคำตอบด้านบนที่โพสต์เมื่อ 4 ปีที่ผ่านมา
gnat

3
@gnat: คำตอบยอดนิยมไม่ได้กล่าวถึงโครงสร้างข้อมูลถาวรหรือรายการที่เชื่อมโยงโดยลำพังเป็นโครงสร้างข้อมูลถาวรที่ง่ายที่สุดหรือว่าจำเป็นสำหรับการเขียนโปรแกรมที่ใช้งานได้จริง ฉันไม่สามารถหาคำตอบที่ทับซ้อนกันได้เลย
Michael Shaw

2

คุณสามารถใช้โหนด Cons ได้อย่างง่ายดายเฉพาะเมื่อคุณมีภาษาที่รวบรวมขยะ

จุดด้อยตรงกับมากกับรูปแบบการเขียนโปรแกรมการทำงานของการโทรซ้ำและค่าไม่เปลี่ยนรูป ดังนั้นมันจึงเหมาะสมกับโมเดลโปรแกรมเมอร์ทางจิต

และอย่าลืมเหตุผลทางประวัติศาสตร์ ทำไมพวกเขาถึงเรียกว่า Cons Nodes และที่แย่กว่านั้นคือยังใช้รถยนต์และ cdr เป็น accessors ผู้คนเรียนรู้จากหนังสือและหลักสูตรแล้วใช้มัน

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


1

รายการที่เชื่อมโยงมีความสำคัญด้วยเหตุผลดังต่อไปนี้:

เมื่อคุณใช้หมายเลขเช่น 3 และแปลงเป็นลำดับตัวตายตัวแทนเช่นsucc(succ(succ(zero)))แล้วใช้การแทนที่ด้วย{succ=List node with some memory space}และ{zero = end of list}คุณจะจบลงด้วยรายการที่เชื่อมโยง (ความยาว 3)

ส่วนที่สำคัญจริงคือตัวเลขการทดแทนและพื้นที่หน่วยความจำและศูนย์

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