เหตุใดอาร์เรย์แบบ zero-based จึงเป็นบรรทัดฐาน?


112

คำถามที่ถามนี่ทำให้ผมนึกถึงของการสนทนาที่ฉันมีกับโปรแกรมเมอร์เพื่อน เขาแย้งว่าควรใช้อาเรย์แบบ zero-based อาเรย์แบบ one-based เนื่องจากอาเรย์ที่เป็นแบบ zero-based นั้นเป็นรายละเอียดการใช้งานที่มีต้นกำเนิดมาจากวิธีการทำงานของอาเรย์และพอยน์เตอร์และการทำงานฮาร์ดแวร์คอมพิวเตอร์ ภาษา

ตอนนี้ฉันไม่ถนัดในการโต้วาทีดังนั้นฉันจึงไม่สามารถเสนอเหตุผลที่ดีในการติดตั้งอาร์เรย์แบบ zero-based นอกเหนือจากที่พวกเขารู้สึกว่าเหมาะสมกว่า เหตุใดจึงเป็นจุดเริ่มต้นทั่วไปของอาร์เรย์


ในอาร์เรย์ n-element องค์ประกอบ 'n' จะไม่ปรากฏ อาร์เรย์ n-element มีสมาชิกที่มีจำนวนตั้งแต่ 0 ถึง n-1 เท่านั้น ดังนั้นมันจะไม่ดีกว่าถ้าเรามีอาร์เรย์ที่เริ่มต้นจาก 1 ดังนั้นอาร์เรย์ n-element จึงแทนองค์ประกอบ n ที่มีอยู่ในอาร์เรย์

ฉันชอบอาร์เรย์ที่มีค่าศูนย์เนื่องจากบิตแรกในไบต์คือ 2 ^ 0 ไม่ใช่ 2 ^ 1 สิ่งนี้ช่วยฉันได้บ้าง :)
e-MEE

ถ้ามีคนดูรายการนี้en.wikipedia.org/wiki/…คน ๆ หนึ่งจะสังเกตเห็นว่าภาษาเฉพาะโดเมนส่วนใหญ่จะเริ่มสร้างดัชนีที่ 1 และภาษาส่วนใหญ่จาก cs ​​school of thinking ที่ 0 หากค้นหานานกว่านี้ เขาจะสังเกตเห็นว่ามีการพูดคุยกันอย่างโง่ ๆ มากมายในเรื่องนี้ซึ่งอาจไม่มีประโยชน์ที่จะลองพูดคุยกับคนทั้งสองเพื่อเปลี่ยนวิธีการของพวกเขา เพื่อปกป้อง "1" คนส่วนใหญ่ในโลกใช้สิ่งนั้น โปรแกรมเมอร์ส่วนใหญ่ใช้ "0" คนส่วนใหญ่ไม่เข้าใจโปรแกรมเมอร์ดังนั้นจึงทำให้คุณคิดว่า
Rook

ฉันไม่อยากจะเชื่อว่าคำถามนี้ได้ถูกโยกย้ายจาก StackOverflow ไปยังโปรแกรมเมอร์แล้วปิดเป็นแบบไม่สร้างสรรค์ นี่เป็นใบ้
paercebal

คำตอบ:


105

ฉันไม่คิดว่าใด ๆ ของเราสามารถให้การโต้แย้งที่แข็งแกร่งกว่าบทความ Edsger ดับบลิวของ Dijkstra "ทำไมเลขควรเริ่มต้นที่ศูนย์"


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

6
บทความของ Dijkstra เป็นเรื่องเกี่ยวกับสไตล์ แต่แล้วข้อโต้แย้งของเขาเกี่ยวกับความเรียบง่ายและใช้งานง่าย ... +1
paercebal

80

อาร์กิวเมนต์สิทธิ์

อืม ... เห็นได้ชัดว่าภาษาส่วนใหญ่รวมถึงภาษาที่เพิ่งผ่านมานั้นไม่มีฐาน เนื่องจากภาษาเหล่านั้นเขียนโดยคนที่มีทักษะค่อนข้างเพื่อนของคุณจะต้องผิด ...

ทำไมหนึ่ง

ทำไม 1 จะเป็นดัชนีเริ่มต้นที่ดีกว่าศูนย์ ทำไมไม่ 2 หรือ 10 คำตอบนั้นน่าสนใจเพราะมันแสดงให้เห็นมากมายเกี่ยวกับกระบวนการคิดของคนที่ปกป้องความคิด

แรกอาร์กิวเมนต์เป็นว่ามันเป็นธรรมชาติมากขึ้นเพราะวันที่ 1มักจะเป็นอย่างใดอย่างหนึ่งก่อนที่คนอื่น ๆ ทั้งหมดอย่างน้อยสำหรับคนส่วนใหญ่ ...

อาร์กิวเมนต์หมายเลขหนึ่งคือดัชนีสุดท้ายยังเป็นขนาดของอาร์เรย์ ...

ฉันยังคงประทับใจใน "คุณภาพ" ของเหตุผลที่ฉันมักจะได้ยินเกี่ยวกับข้อโต้แย้งประเภทนี้ ... และยิ่งกว่านั้นเมื่อฉันนึกถึงว่า ...

ทำไมไม่เป็นศูนย์

... สัญลักษณ์ "One-based" นั้นเหลือจากวัฒนธรรมตะวันตกที่เพิกเฉยต่อการดำรงอยู่ของศูนย์มานานหลายศตวรรษถ้าไม่มาก

เชื่อหรือไม่ว่าปฏิทินเกรกอเรียนดั้งเดิมเริ่มจาก -3, -2, -1, 1, 2, 3 ... ลองนึกภาพปัญหาที่เกิดขึ้นกับวิทยาศาสตร์ตะวันตก (ตัวอย่างเช่นกี่ปีตั้งแต่ 1 มกราคม -2 ถึงวันที่ 1 มกราคม 2 เพื่อดูมากกว่าปฏิทินเกรกอเรียนดั้งเดิมที่ขัดแย้งกับสิ่งที่ง่ายเหมือนการลบ ... )

การรักษาอาร์เรย์หนึ่งให้เป็นเหมือน (เอาล่ะฉันจะถูกลดทอนเพราะเรื่องนั้น ... ^ _ ^ ... ) โดยรักษาระยะทางหลายไมล์ในศตวรรษที่ 21 ...

ทำไมต้องเป็นศูนย์? เพราะมันเป็นคณิตศาสตร์!

ครั้งแรก (OOops ... ขออภัย ... ฉันจะลองอีกครั้ง)

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

ขั้นแรกให้ทำงานกับอาร์เรย์ zero-based ได้ง่ายขึ้นและละเว้นค่า zero-th ของมันง่ายกว่าทำงานกับ array ที่ใช้หนึ่งและแฮ็คเพื่อค้นหาค่า zero-th เหตุผลนี้เกือบจะโง่เหมือนก่อนหน้านี้ แต่จากนั้นอาร์กิวเมนต์ดั้งเดิมที่สนับสนุนอาเรย์หนึ่งที่ใช้ก็เป็นความผิดพลาดเช่นกัน

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

สามสำหรับอาร์เรย์ภาษาคอมพิวเตอร์ที่เชื่อมโยงกับฮาร์ดแวร์จัดสรรอาร์เรย์ C จำนวนเต็ม 21 ตัวและเลื่อนตัวชี้ 10 ดัชนีไปทางขวาและคุณจะมีอาร์เรย์ [-10 ถึง 10] ตามธรรมชาติ สิ่งนี้ไม่เป็นธรรมชาติสำหรับฮาร์ดแวร์ แต่สำหรับคณิตศาสตร์ แน่นอนคณิตศาสตร์อาจล้าสมัย แต่ครั้งสุดท้ายที่ฉันตรวจสอบคนส่วนใหญ่ในโลกเชื่อว่าไม่ใช่

สี่ , ตามที่ชี้ไปแล้วที่อื่น, แม้สำหรับตำแหน่งที่ไม่ต่อเนื่อง (หรือระยะทางลดลงเป็นค่าไม่ต่อเนื่อง), ดัชนีแรกจะเป็นศูนย์, เช่นพื้นในอาคาร (เริ่มต้นที่ศูนย์), นับถอยหลังลดลง (3, 2, 1, ศูนย์ !) ระดับความสูงของพื้นดินพิกเซลแรกของภาพอุณหภูมิ (ศูนย์เคลวินสำหรับศูนย์สัมบูรณ์หรือองศาเซนติเกรดศูนย์เป็นอุณหภูมิน้ำแช่แข็ง 273 K) ในความเป็นจริงสิ่งเดียวที่มันเริ่มต้นด้วยหนึ่งเป็นวิธีการแบบดั้งเดิมของ " ครั้งแรก , สอง , สาม , ฯลฯ" สัญกรณ์ซ้ำซึ่งทำให้ฉันตามธรรมชาติไปยังจุดต่อไป ...

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

ข้อสรุป

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

zero-based arrays เป็นแบบ zero-based เนื่องจากเหตุผลที่เกี่ยวข้องกับคณิตศาสตร์ ไม่ใช่ด้วยเหตุผลที่เกี่ยวข้องกับฮาร์ดแวร์

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


องศาเซนติเกรดเป็นศูนย์คือ 273,15K;)

2
ฉันรู้ (ฉันมีอนุปริญญาโทสาขาฟิสิกส์) แต่ฉันรู้สึกว่าการเล่นกับทศนิยมนั้นน้อยกว่าจุดที่มีอารมณ์ขันที่ฉันพยายามทำสีข้อโต้แย้งของฉันด้วย ... ^ _ ^ ...
paercebal

20
ย่อหน้าของคุณมีป้ายกำกับว่า "Zero, First, Second, Third, Four, Five" เพื่อความมั่นคงคุณควรใช้หมายเลขที่สำคัญ ("ศูนย์, หนึ่ง, สอง, สาม, สี่") หรือเลขลำดับ ("Zeroth, First, Second, Second, Third, Fourth, Fifth") :-)
ShreevatsaR

10
ในทำนองเดียวกันสำหรับปีแรกของชีวิตของเราเราไม่ได้อายุหนึ่งปี แต่เป็นศูนย์ปี

3
@Nikita Rybak: สิ่งที่น่าอัศจรรย์คือคุณพลาดสิ่งที่ผู้วิจารณ์ทุกคนเห็นมาก่อน: แน่นอนว่าคำตอบของ Bill the Lizard นั้นถูกต้อง นี่คือเหตุผลที่ฉันโหวตให้เขา +1 และนี่คือเหตุผลที่มันถูกเลือกให้เป็นคำตอบที่ดีที่สุดของคำถาม คำตอบของฉันคือเพิ่มเติมเกี่ยวกับการสร้างความสนุกด้วยเหตุผลที่ผิดพลาดที่อยู่เบื้องหลังอาร์เรย์ 1 ฐานและเสนอกรณีที่เป็นรูปธรรมซึ่งอาร์เรย์ 1-based จะสร้างความรำคาญ ถึงกระนั้นฉันก็ประหลาดใจที่คุณพบว่า "ไม่ใช่คนที่น่าเชื่อถือ" แม้แต่เมื่อพิจารณาถึงเหตุผลที่ผสมกับการเสียดสี ...
paercebal

47

ช่วงเวลาเปิดครึ่งเขียนได้ดี หากคุณกำลังจัดการใน0 <= i < limและคุณต้องการที่จะขยายโดยองค์ประกอบองค์ประกอบใหม่มีดัชนีในช่วงn lim <= i < lim + nการทำงานกับ zero-based อาร์เรย์ทำให้คณิตศาสตร์ง่ายขึ้นเมื่อแยกหรือเชื่อมโยงอาร์เรย์หรือเมื่อนับองค์ประกอบ หนึ่งหวังเลขคณิตง่ายนำไปสู่ข้อผิดพลาดน้อยลง fencepost


+1 สำหรับช่วงครึ่งเวลา - มันทำให้ทุกอย่างง่ายขึ้น
Eclipse

29

การจัดการอาเรย์บางประเภททำให้เกิดความซับซ้อนอย่างบ้าคลั่งด้วยอาเรย์ 1 แบบ แต่จะง่ายกว่าด้วยอาเรย์แบบ 0

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

อัลกอริทึมของ FORTRAN นั้นมีจำนวนมากa[i + j + k - 2]ในขณะที่ C ++ นั้นมีa[i + j + k]เพราะอาร์เรย์ของ FORTRAN นั้นใช้แบบ 1 ส่วนในขณะที่อาร์เรย์ของ C ++ นั้นจะเป็นแบบ 0


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

ฉันได้รับความยุ่งยากที่ไม่จำเป็นของ 1 อาร์เรย์ตามเช่นกัน 0 ตามอาร์เรย์มีในประสบการณ์ที่ จำกัด ของฉันมักจะผลิตรหัสที่ชัดเจนสำหรับการทำดัชนีอาร์เรย์ด้วยข้อยกเว้นที่หายาก

ดัชนี FORTRAN และ C ++ แตกต่างกันอย่างไร 2 หากดัชนีที่เกี่ยวข้องถูกชดเชยด้วย 1 เท่านั้น ทำไมต้องลบ 2 ถ้า FORTRAN ใช้ 1 คุณจะไม่เพิ่ม 2 (หรือ 1)
RexE

@RexE: นั่นเป็นวิธีการทำงานและนั่นเป็นเหตุผลว่าทำไมมันซับซ้อนมากด้วย 1-based arrays
Jay Bazuzi

@RexE: สมมติว่าคุณจำลองอาร์เรย์ 3 มิติด้วยแบน จากนั้นใน 0 ฐานองค์ประกอบ (0 0 0) สอดคล้องกับองค์ประกอบ 0 ในลำดับแบน OTOH ถ้าเป็น 1 องค์ประกอบ (1 1 1) จะสอดคล้องกับองค์ประกอบที่ 1 ในอาเรย์แบน: 1 + 1 + 1-2

22

ดัชนีในอาร์เรย์ไม่ได้เป็นดัชนีจริงๆ มันเป็นเพียงการชดเชยที่เป็นระยะทางจากจุดเริ่มต้นของอาร์เรย์ องค์ประกอบแรกอยู่ที่จุดเริ่มต้นของอาร์เรย์จึงไม่มีระยะทาง ดังนั้นการชดเชยคือ 0


3
สำหรับภาษาส่วนใหญ่ที่ได้รับการออกแบบในปัจจุบันนี้เป็นจริงรายละเอียดการดำเนินงานซึ่งไม่ควรปรากฏในภาษา (ยกเว้นเมื่อมีเหตุผลอื่นที่ดีกว่าที่จะทำ)
Jens Schauder

17

เหตุผลไม่ได้เป็นเพียงข้อมูลเชิงประวัติ: C และ C ++ ยังคงมีอยู่และใช้กันอย่างแพร่หลายและการคำนวณทางคณิตศาสตร์เป็นเหตุผลที่ถูกต้องมากสำหรับการเริ่มต้นอาร์เรย์ที่ดัชนี 0

สำหรับภาษาอื่นที่ไม่มีตัวชี้เลขคณิตไม่ว่าองค์ประกอบแรกจะอยู่ที่ดัชนี 0 หรือ 1 เป็นการประชุมมากกว่าสิ่งอื่นใด
ปัญหาคือภาษาที่ใช้ดัชนี 1 เป็นองค์ประกอบแรกของพวกเขาไม่ได้อยู่ในสุญญากาศและมักจะต้องโต้ตอบกับห้องสมุดที่มักจะเขียนใน - คุณเดามัน - C หรือ C ++ ...

VB และรสชาติที่ได้รับนั้นได้รับความเดือดร้อนจากการมีอาร์เรย์เริ่มต้นที่ 0 หรือ 1 และเป็นสาเหตุของปัญหามาเป็นเวลานาน

บรรทัดล่างคือ: ไม่สำคัญว่าภาษาของคุณจะพิจารณาดัชนีองค์ประกอบแรกตราบใดที่มันสอดคล้องกันตลอด ปัญหาคือการพิจารณา 1 เป็นดัชนีแรกทำให้การทำงานในทางปฏิบัติยากขึ้น


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

ในขณะที่เราอยู่ที่นี่คำถาม: คุณเคยใช้รหัสระดับต่ำในวิธีเฉพาะที่ไม่ใช่แพลตฟอร์มหรือไม่? พูดอีกอย่างคือคุณอยู่บนแพลตฟอร์มใดแพลตฟอร์มหนึ่งเสมอและคุณต้องรู้ว่าใช่ไหม?
Dan Rosenstark

1
ในฐานะที่เป็นคนที่คิดว่า VB .NET นั้นมักจะไม่ถูกต้องอย่างไม่ยุติธรรมฉันต้องบอกว่าแนวปฏิบัติของ VB .NET ในอาร์เรย์นั้นแย่มาก พวกเขาแยกความแตกต่างและทำให้มันสับสนยิ่งขึ้น: อาร์เรย์เริ่มต้นที่ 0 แต่ Dim a as Integer (5) สร้างอาร์เรย์ที่มี6ตำแหน่ง เหตุผลดูเหมือนว่าการมีตำแหน่งพิเศษนั้นดีกว่าการมีข้อบกพร่องจากการพูดผ่านความยาวของอาเรย์ น่าเสียดายที่ (และปัญหาอื่น ๆ เช่นและและเป็นบิต) พวกเขาคาดหวังกับความต้องการจากโปรแกรมเมอร์ VB6 มากมายที่ไม่ได้ลงเอยด้วยการใช้ VB .NET
Kyralessa

1
@Kyralessa: ไม่เหตุผลก็คือต้องมีความเข้ากันได้ย้อนหลังกับ VB6 (ผู้ช่วยอัปเกรดอัตโนมัติ…) แม้ว่าพวกเขาจะทราบดีว่าสัญกรณ์นั้นเป็นแบบที่ใช้งานง่ายและผิดพลาดได้ง่าย ในทางตรงกันข้ามAndและOrการ bitwise ไม่มีอะไรเกี่ยวข้องกับ VB6 มันเป็นทางออกเดียวสำหรับภาษาประเภท VB คุณไม่ได้AndAlsoและOrElseสำหรับการดำเนินงานเชิงตรรกะของคุณ
Konrad Rudolph

AndและOrการอยู่ในระดับบิตมีทุกอย่างที่เกี่ยวข้องกับ VB6 เพราะมันเป็นบิตใน VB6 ผู้ประกอบการที่น่าเกลียดAndAlsoและOrElseควรได้รับการทำในระดับบิตเนื่องจากการดำเนินงานในระดับบิตเป็นเรื่องธรรมดาน้อยกว่าตรรกะ มีหูดที่น่าเกลียดมากมายเช่นที่เหลืออยู่ในภาษาเนื่องจาก "ความเข้ากันได้ย้อนหลัง" เช่นความจริงที่ว่า ByVal ถูกฉาบทั่วทุกที่แม้ว่ามันจะเป็นค่าเริ่มต้น
Kyralessa

10

อาร์เรย์แบบ zero-based มีรากฐานใน C และแม้แต่แอสเซมเบลอร์ ด้วย C ตัวชี้ทางคณิตศาสตร์จะทำงานดังนี้:

  • แต่ละองค์ประกอบของอาร์เรย์ใช้จำนวนไบต์ที่แน่นอน จำนวนเต็ม 32 บิตคือ (ชัด) 4 ไบต์;
  • ที่อยู่ของอาเรย์นั้นถูกครอบครองโดยองค์ประกอบแรกของอาเรย์ที่มีองค์ประกอบที่ตามมาในบล็อกที่ต่อเนื่องกันขนาดเท่ากันหลังจากนั้น

เพื่อแสดงให้เห็นว่าสมมุติint a[4]อยู่ที่ 0xFF00 ที่อยู่คือ:

  • a [0] -> 0xFF00;
  • a [1] -> 0xFF04;
  • a [2] -> 0xFF08;
  • a [3] -> 0xFF0C

ดังนั้นด้วยดัชนีตามศูนย์คณิตศาสตร์ addres นั้นง่าย:

ที่อยู่ขององค์ประกอบ = ที่อยู่ของอาร์เรย์ + ดัชนี * sizeof (ประเภท)

ในความเป็นจริงแล้วนิพจน์ใน C นั้นเทียบเท่ากันทั้งหมด:

  • a [2];
  • 2 [เป็น]; และ
  • * (A + 2)

ด้วยอาร์เรย์ที่ใช้หนึ่งเดียวคณิตศาสตร์นั้นซับซ้อนกว่าเดิมเล็กน้อย

ดังนั้นเหตุผลส่วนใหญ่เป็นประวัติศาสตร์


1
คำถามเดิมกล่าวแล้วว่า "อาร์เรย์ที่มีค่าเป็นศูนย์นั้นเป็นรายละเอียดการใช้งานที่มีต้นกำเนิดมาจากวิธีที่อาร์เรย์และพอยน์เตอร์และฮาร์ดแวร์คอมพิวเตอร์ทำงาน แต่สิ่งเหล่านี้ไม่ควรสะท้อนในภาษาระดับสูงกว่า"

เป็นมูลค่าการกล่าวถึงว่าภาษาที่อนุญาตให้อาร์เรย์ที่ใช้ N มักจะสร้างรหัสที่มีการคำนวณ 'offsets' ของอาร์เรย์โดยอัตโนมัติที่ศูนย์ต้นทุนรันไทม์
Roddy

8

ถ้าคุณใช้อาร์เรย์แบบ zero-based ความยาวของอาร์เรย์คือชุดของดัชนีที่ถูกต้อง อย่างน้อยนั่นคือสิ่งที่ Peano เลขคณิตพูดว่า:

0 = {}
1 = 0 U {0} = {0}
2 = 1 U {1} = {0,1}
3 = 2 U {2} = {0,1,2}
...
n = n-1 U {n-1} = {0,1,2...n-1}

ดังนั้นมันจึงเป็นสัญกรณ์ธรรมชาติที่สุดในแง่หนึ่ง


7

เนื่องจากมีความสัมพันธ์ที่แข็งแกร่งระหว่างอาร์เรย์และตัวชี้ใน C

char* p = "hello";
char q[] = "hello";

assert(p[1] == q[1]);

assert(*p == *q)

* p เหมือนกับ * (p + 0)

การมีดัชนีเริ่มต้นที่ 1 จะทำให้คุณปวดหัวในภายหลัง


5

ฮีปเป็นตัวอย่างหนึ่งของข้อดีของอาร์เรย์ที่ใช้ 1 รับดัชนีฉันดัชนีของฉัน 's แม่และเด็กเป็นซ้าย

PARENT[ i ] = i ÷ 2

LCHILD[ i ] = i × 2

แต่สำหรับอาร์เรย์ที่ใช้ 1 เท่านั้น สำหรับอาร์เรย์ 0 รายการที่คุณมี

PARENT[ i ] = ( i + 1) ÷ 2 - 1

LCHILD[ i ] = ( i + 1) × 2 - 1

แล้วคุณมีคุณสมบัติที่ฉันยังเป็นขนาดของอาร์เรย์ย่อยไปยังดัชนีนั้น (เช่นดัชนีในช่วง [1, i ])

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


4

ความรู้สึกของฉันคือมันเป็นสิ่งที่ไม่มีเหตุผล ไม่มีอะไรพิเศษเกี่ยวกับอาร์เรย์หรือศูนย์หนึ่ง เนื่องจากการปลดปล่อยตัวเองจาก Visual Basic (ส่วนใหญ่บางครั้งฉันทำสิ่งเล็ก ๆ ใน Excel) ฉันไม่ได้ทำงานกับอาร์เรย์ที่ใช้ 1 และ ... มันเหมือนกัน ความจริงก็คือถ้าคุณต้องการองค์ประกอบที่สามของอาร์เรย์มันเป็นเพียงรายละเอียดการนำไปปฏิบัติที่เรียกว่า 3 หรือ 2 อย่างไรก็ตาม 99% ของงานที่คุณทำกับอาร์เรย์นั้นสนใจเพียงสองจุดเท่านั้น: องค์ประกอบแรกและ การนับหรือความยาว อีกครั้งเป็นเพียงรายละเอียดการใช้งานที่องค์ประกอบแรกเรียกว่าศูนย์แทนหนึ่งหรือว่าองค์ประกอบสุดท้ายเรียกว่า count-1 หรือแทนนับ

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


ใน PHP การค้นหาส่วนใหญ่ภายในฟังก์ชั่นสตริงคืนค่า FALSE ไม่พบ ไม่ใช่ -1
jmucchiello

คุณกำลังสับสนกับการนับดัชนีขององค์ประกอบสุดท้าย จำนวนของอาเรย์ที่ว่างเปล่าจะเป็น 0 เสมอไม่ว่าคุณจะใช้อาร์เรย์แบบ zero-based หรือ one-based ข้อได้เปรียบของอาร์เรย์แบบใช้ครั้งเดียวคือการนับเป็นตำแหน่งขององค์ประกอบสุดท้าย (แต่นั่นเป็นเรื่องเกี่ยวกับความได้เปรียบเพียงอย่างเดียว)

จริงเกี่ยวกับคะแนนทั้งสองนี้: ถูกลบไปครึ่งหลังเนื่องจากมันเหมือนกันสำหรับอาร์เรย์ที่ใช้ zero-based หรือ one-based: นับเป็น 0 ถ้าคุณมีองค์ประกอบที่เป็นศูนย์
Dan Rosenstark

ฉันหมายถึงครึ่งหลังของคำตอบของฉัน ...
แดน Rosenstark

4

ในฐานะโปรแกรมเมอร์ 10 + ปี C / C ++ ที่มีพื้นหลังที่แข็งแกร่งใน Pascal และ Delphi ฉันยังคงพลาดการตรวจสอบขอบเขตและการตรวจสอบประเภทดัชนีที่แข็งแกร่งของ Pascal และความยืดหยุ่นและความปลอดภัยที่มาพร้อมกับมัน ตัวอย่างที่ชัดเจนของเรื่องนี้คือค่าการเก็บข้อมูลอาร์เรย์สำหรับแต่ละเดือน

ปาสคาล:

 Type Month = (Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec);

  Var Days[Month] of integer;

  ... 
  if Year mod 4 = 0 then // yes this is vastly simplified for leap years and yes i don't know what the comment marker is in pascal and no i won't go look it up
    Days[Feb] := 29
  else
    Days[Feb] := 28;

การเขียนโค้ดที่คล้ายกันในภาษา C โดยไม่ต้องใช้ +/- 1 หรือ 'magic number' นั้นค่อนข้างท้าทาย โปรดทราบว่าการแสดงออกเช่น Days [2] และ Days [Jan + Dec] จะไม่รวบรวมไว้ซึ่งอาจดูโหดร้ายสำหรับผู้ที่ยังคิดใน C หรือ Assembler

ฉันต้องบอกว่ามีหลายแง่มุมของภาษา Pascal / Delphi ที่ฉันไม่ควรพลาดนิดหน่อย แต่ C Array แบบ zero-based ดูเหมือนจะ "โง่" โดยการเปรียบเทียบ


อาจเป็นที่น่าสังเกตว่าอัลกอริทึมของคุณไม่ถูกต้องสำหรับปี 2100 en.wikipedia.org/wiki/Leap_year#Algorithm

2
ฉันรู้ ;-) อย่างไรก็ตามมันถูกต้องสำหรับปี 2000 ฉันแค่เล่น "spot the pedant" ...
Roddy

จุดคนอวดรู้! ฮ่า ๆ.
jcollum

ใช่. หลีกเลี่ยงปัญหาทั้งหมดตั้งค่าอาร์เรย์ตามที่คุณต้องการ
Loren Pechtel

ฉันจะไม่ต้องแปลกใจถ้าคอมไพเลอร์ปาสกาลเฉลี่ยของคุณกำหนด ม.ค. = 0 ธ.ค. = 11 เมื่อสร้าง :-) รหัสเครื่อง

4

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

อีกวิธีหนึ่งในการดูว่าสิ่งเหล่านี้เทียบเท่า (ส่วนใหญ่):

array[n]

*(array + n)

เหตุผลที่มาตรฐานจะไม่เปลี่ยนแปลงเพราะ C ได้รับรอบประมาณ 40 ปีแล้ว ไม่มีเหตุผลที่น่าสนใจที่จะเปลี่ยนแปลงและถ้าพวกเขาทำทุกรหัสที่มีอยู่ที่ขึ้นอยู่กับจุดเริ่มต้นของอาร์เรย์ที่เป็น 0 จะแตก


ในความเป็นจริงคุณสามารถเขียนซ้ำarray[n]เหมือนn[array]ใน C. ไม่ใช่ความคิดที่ดีที่จะทำมันสับสน! แต่มันถูกกฎหมาย (อย่างน้อยก็มากถึง C89) เนื่องจากตัวตนข้างต้นและความจริงที่ว่าการเติมเป็นแบบสลับกัน
Donal Fellows

นั่นเป็นวิธีที่บ้าในการเขียนสิ่งที่ถ้าคุณเห็นในรหัสใด ๆ ที่คุณต้องรักษาจะเป็นสัญญาณเตือนภัยขนาดใหญ่ โชคดีที่ฉันยังไม่ได้เจอเลย ... แต่ :)
Dennis Munsie

4

รหัสรวมถึงตำแหน่งเดิม / ข้อมูลตำแหน่งสัมพัทธ์มีความสะอาดมากขึ้นโดยมีอาร์เรย์เริ่มต้นที่ 0

ตัวอย่างเช่น: รหัสที่จะคัดลอกเวกเตอร์ที่ตำแหน่งที่กำหนดในเวกเตอร์ที่ใหญ่กว่านั้นเป็นความเจ็บปวดโดยที่อาร์เรย์เริ่มต้นที่ 1:

function copyAtPos (dest, vect, i):
    for i from 1 -> vect.length do
        dest[pos+i-1] = vect[i]

โดยฝ่ายค้านกับอาร์เรย์เริ่มต้นที่ 0:

function copyAtPos (dest, vect, i):
    for i from 0 -> vect.length-1 do
        dest[pos+i] = vect[i]

หากคุณเริ่มเขียนสูตร convolutions ที่ยิ่งใหญ่มันจะกลายเป็นสิ่งจำเป็น


3

ทำไมไม่ 2 หรือ 3 หรือ 20 มันไม่เหมือนกับการมีอาร์เรย์ที่ใช้ 1 เป็นวิธีที่ง่ายกว่าหรือง่ายกว่าที่จะเข้าใจแล้วใช้อาร์เรย์ที่มีศูนย์ ในการเปลี่ยนไปใช้อาร์เรย์แบบ 1 นั้นโปรแกรมเมอร์ทุกคนจะต้องเรียนรู้วิธีการทำงานกับอาร์เรย์อีกครั้ง

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

และบางครั้งคุณต้องจัดการกับข้อมูลจำนวนหนึ่งในอาร์เรย์แม้เป็นภาษาที่ไม่มีตัวชี้ทางคณิตศาสตร์ "จริง" ในจาวาคุณสามารถมีข้อมูลในไฟล์ที่แม็พหน่วยความจำหรือบัฟเฟอร์ ในกรณีดังกล่าวคุณรู้ว่า block i มีขนาดเป็น * i ด้วยดัชนีแบบ 1 มันจะอยู่ที่ block * i + 1

ด้วยการจัดทำดัชนีแบบอิง 1 เทคนิคมากมายจะต้องมี +1 ทั่วทุกสถานที่


ทำไมไม่ 2 หรือ 3 หรือ 20 เพราะ 0 คือตัวเสริมและ 1 คือตัวตนแบบทวีคูณ พวกเขาทำให้รู้สึกมากที่สุด

3

การใช้อาร์เรย์ที่ใช้ 1 แปลงอาร์เรย์มิติเดียวเป็นอาร์เรย์หลายมิติ:

int w = 5, h = 5, d = 5;

int[] a1 = new int[w * h * d], new a2 = int[w,h,d];

for (int z = 1; z <= d; z++)

  for (int y = 1; y <= h; y++)

    for (int x = 1; x <= w; x++)

      a1[x + (y - 1) * w + (z - 1) * h] = a2[x,y,z];

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


3

ทำไมคุณต้องการให้อาร์เรย์เริ่มที่หนึ่ง

เมื่อคุณพูดเรียบเรียงแปลนี้เป็น:a[x][y] หากอาร์เรย์เริ่มต้นที่หนึ่งนี้จะกลายเป็นa+(x*num_cols+y) a+(x*num_cols+y-1)นี่จะเป็นการดำเนินการทางคณิตศาสตร์พิเศษทุกครั้งที่คุณต้องการเข้าถึงองค์ประกอบอาร์เรย์ ทำไมคุณต้องการที่จะชะลอตัวโปรแกรม?


1
ที่จริงแล้วมันจะต้องกลายเป็น + ((x - 1) * num_cols) + y - 1) - ทั้ง x และ y จะเริ่มต้นจาก 1
Dennis Munsie

2

ฉันจะก้าวออกมาจากกิ่งที่นี่และแนะนำสิ่งที่แตกต่างจากอาร์เรย์ 'คีย์' จำนวนเต็ม

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

คำแนะนำของฉัน

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

ie kvp['Name Server'] = "ns1.example.com"; (นี่เป็นเพียงตัวอย่างหนึ่งในล้านตัวอย่างที่เป็นไปได้)

Discaimer

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

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


2

โดยส่วนตัวแล้วอาร์กิวเมนต์หนึ่งคือเมื่อเห็นดัชนีอาร์เรย์เป็นออฟเซ็ต มันสมเหตุสมผลแล้ว

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

ดังนั้นในการคำนวณมันง่ายที่จะเพิ่มศูนย์เพื่อค้นหาองค์ประกอบแรกกว่าที่จะเพิ่มหนึ่งและจากนั้นลบหนึ่ง

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


เผง - มันเป็นการประชุมที่วางไว้จากภาษาระดับต่ำกว่า

2

เพียงสอง (มาก) ด้วยเหตุผลอย่างร้ายแรงต่อการใช้ 0-based ดัชนีแทน 1 ตามดัชนีดูเหมือนจะหลีกเลี่ยง reeducating จำนวนมาก programersและสำหรับการเข้ากันได้ย้อนหลัง

ฉันไม่เห็นข้อโต้แย้งที่ร้ายแรงใด ๆ เทียบกับดัชนี 1 ฐานในคำตอบทั้งหมดที่คุณได้รับ

ในความเป็นจริงดัชนีมีพื้นฐานตามธรรมชาติ 1ประการและนี่คือสาเหตุ

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

ในโลกแห่งความเป็นจริงดัชนีเป็น 1 ฐาน

ดังที่โทมัสกล่าวไว้ข้างต้นภาษาที่ใช้ดัชนีฐาน 0 อันที่จริงแล้วเป็นการใช้งานออฟเซ็ตไม่ใช่ดัชนี และนักพัฒนาซอฟต์แวร์ที่ใช้ภาษาเหล่านี้จะคิดเกี่ยวกับออฟเซ็ตไม่ใช่ดัชนี สิ่งนี้จะไม่เป็นปัญหาหากมีการระบุอย่างชัดเจน แต่ไม่ใช่ นักพัฒนาซอฟต์แวร์จำนวนมากที่ใช้ออฟเซ็ตยังคงพูดคุยเกี่ยวกับดัชนี และนักพัฒนาจำนวนมากที่ใช้ดัชนียังไม่ทราบว่า C, C ++, C #, ... ใช้ offsets

นี่คือปัญหาการใช้ถ้อยคำ

(หมายเหตุเกี่ยวกับกระดาษของ Diskstra - มันบอกว่าสิ่งที่ฉันได้กล่าวข้างต้น : นักคณิตศาสตร์ใช้ดัชนี 1-basedแต่ Diskstra คิดว่า matematicians ไม่ควรใช้พวกเขาเพราะการแสดงออกบางอย่างจะน่าเกลียด (เช่น: 1 <= n <= 0 ). ไม่แน่ใจว่าเขาถูกต้องในเรื่องนั้น - การทำกระบวนทัศน์แบบนี้เพื่อหลีกเลี่ยงการวนเวียนเปล่าที่พิเศษเหล่านั้นดูเหมือนจะเป็นปัญหามากสำหรับผลเพียงเล็กน้อย ... )


2
นักคณิตศาสตร์ไม่ได้ใช้ดัชนี 1 เสมอ ฉันเห็นว่า x0 ใช้เวลามากมายสำหรับค่าเริ่มต้นของลำดับ ขึ้นอยู่กับว่าสะดวกที่ไหน

2

คุณเคยรู้สึกรำคาญกับ "ศตวรรษที่ 20" จริงๆแล้วหมายถึงยุค 1900 หรือไม่? มันเป็นการเปรียบเทียบที่ดีสำหรับสิ่งที่น่าเบื่อที่คุณจัดการตลอดเวลาเมื่อใช้อาร์เรย์ 1 ตัว

พิจารณางานอาร์เรย์ทั่วไปเช่นวิธีการอ่าน. net IO.stream:

int Read(byte[] buffer, int offset, int length)

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

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

ทีนี้การปรับใช้แบบใดแบบหนึ่งที่ง่ายกว่า? สิ่งใดที่มีการชดเชย +1 และ -1 โรยที่นี่และที่นั่น? นั่นคือสิ่งที่ฉันคิดว่า. ในความเป็นจริงฉันจะยืนยันว่ากรณีเดียวที่สไตล์การจัดทำดัชนีไม่สำคัญคือเมื่อคุณควรใช้สิ่งที่ไม่ใช่อาร์เรย์เช่นชุด


มันเป็นตรรกะที่ไม่ดีเปรียบเทียบจำนวนเต็มสับสนกับจุดลอย

2

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

base + n * element_size

ดังนั้น 0 จึงเป็นค่าชดเชยแรก


1

จริงๆแล้วมีหลายวิธีที่จะใช้สิ่งนี้:

  • ดัชนีอาร์เรย์ 0
  • ดัชนีอาร์เรย์แบบ 1
  • 0 หรือ 1 อาร์เรย์ตาม (เช่น VB 6.0 ... มันน่ากลัวจริง ๆ )

ในที่สุดฉันไม่คิดว่ามันสำคัญว่าภาษาจะใช้อาร์เรย์แบบ 0 หรือ 1 แต่ฉันคิดว่าทางเลือกที่ดีที่สุดคือการใช้อาร์เรย์ที่ใช้ 0 เนื่องจากเหตุผลง่ายๆที่โปรแกรมเมอร์ส่วนใหญ่คุ้นเคยกับการประชุมนั้นและสอดคล้องกับโค้ดส่วนใหญ่ที่เขียนไว้แล้ว

วิธีเดียวที่คุณจะผิดพลาดได้จริง ๆ ก็คือการไม่สอดคล้องเช่น Visual Basic รหัสฐานที่ฉันกำลังรักษาอยู่จะถูกแบ่งระหว่าง 0 และ 1 อาร์เรย์ตาม และมันยากเหลือเกินที่จะคิดออกว่าอันไหน สิ่งนี้นำไปสู่ ​​verbose ที่น่ารำคาญสำหรับ loop:

dim i as integer, lb as integer, ub as integer
lb = LBound(array)
ub = UBound(array)
for i = lb to ub
       '...
next

ฮ่าฮ่าฮ่าฉันจำได้ว่าคนที่ดูด ...
Dan Rosenstark

ฉันคิดว่าฉันจำได้ว่ามีอาร์เรย์ที่เริ่มต้นด้วยจำนวนลบ เพียงหนึ่งในหลายเหตุผลที่ฉันอยู่ห่างจาก VB

1

ซีโร่เป็นธรรมชาติเมื่อพูดถึงที่ตั้งของไอเท็มในคอลเลกชันเชิงเส้น

ลองนึกถึงชั้นวางหนังสือที่เต็มไปด้วยหนังสือเล่มแรกตั้งอยู่ติดกับผนังด้านข้างของชั้นวางนั่นคือตำแหน่งศูนย์

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


1

ฉันชอบดัชนีตาม 0 ตั้งแต่โมดูโล่ (และตัวดำเนินการ AND เมื่อใช้สำหรับโมดูโล) จะส่งคืน 0 สำหรับค่าบางค่าเสมอ

ฉันมักจะพบว่าตัวเองใช้อาร์เรย์เช่นนี้

int blah = array[i & 0xff];

ฉันมักจะได้รหัสที่ผิดเมื่อใช้ 1 ดัชนีตาม


1

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

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

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

อีกตัวอย่างหนึ่งคือวิธีใช้ "goto" ในภาษาที่คุณไม่มีทางเลือกเช่นไฟล์แบตช์ MS-DOS แนวทาง "สามัญสำนึก" คือการแนบป้ายกำกับไปยังบล็อกของรหัสที่ต้องทำและติดป้ายกำกับดังกล่าว บ่อยครั้งวิธีที่ดีกว่าคือการใส่ป้ายกำกับไว้ที่ส่วนท้ายของบล็อกของรหัสเพื่อจุดประสงค์ในการข้ามไป สิ่งนี้ทำให้ "มีโครงสร้าง" และง่ายต่อการปรับเปลี่ยนมาก


1

มันเป็นเช่นนั้นมานานหลายปีแล้ว หากต้องการเปลี่ยนมันหรือแม้กระทั่งการถกเถียงมันก็ไร้ประโยชน์เท่า ๆ กับการเปลี่ยนหรือถกเถียงการเปลี่ยนสัญญาณไฟจราจร มาทำให้สีฟ้าหยุด = แดง = ไป

ดูการเปลี่ยนแปลงที่เกิดขึ้นในช่วงเวลาในสูตรตัวเลขสำหรับ C ++ พวกเขาใช้มาโครในการปลอมดัชนี 1 รายการ แต่ในปี 2001 ฉบับยอมแพ้และเข้าร่วมฝูง อาจมีการแจ้งข้อมูลเกี่ยวกับเหตุผลที่อยู่เบื้องหลังสิ่งนี้ที่เว็บไซต์ของพวกเขา www.nr.com

BTW ที่น่ารำคาญก็คือตัวแปรในการระบุช่วงของอาร์เรย์ ตัวอย่าง: python vs. IDL; a [100: 200] vs a [100: 199] เพื่อรับองค์ประกอบ 100 ชิ้น เพียงแค่ต้องเรียนรู้นิสัยของแต่ละภาษา หากต้องการเปลี่ยนภาษาที่ใช้วิธีจับคู่กับอีกวิธีหนึ่งจะทำให้เกิดการสบประมาทและขบเขี้ยวเคี้ยวฟันและไม่แก้ปัญหาที่แท้จริง


1

ฉันชอบอาร์เรย์ที่ใช้ 0 เพราะตามที่คนอื่นพูดถึงมันทำให้คณิตศาสตร์ง่ายขึ้น ตัวอย่างเช่นถ้าเรามีอาร์เรย์ 1 มิติ 100 องค์ประกอบเลียนแบบตาราง 10x10 ดังนั้นดัชนีอาร์เรย์ i ขององค์ประกอบในแถว r คืออะไร, c c:

0-based: i = 10 * r + c
1-based: i = 10 * (r - 1) + c

และเมื่อให้ดัชนี i กลับไปที่แถวและคอลัมน์คือ

เป็นพื้นฐาน 0: c = i% 10
         r = floor (i / 10)
แบบ 1: c = (i - 1)% 10 + 1
         r = ceil (i / 10)

เนื่องจากคณิตศาสตร์ดังกล่าวมีความซับซ้อนมากขึ้นเมื่อใช้อาร์เรย์ที่มีพื้นฐาน 1 ตัวจึงมีเหตุผลที่จะเลือกอาร์เรย์ที่ใช้ 0 เป็นมาตรฐาน

อย่างไรก็ตามฉันคิดว่ามีใครบางคนสามารถอ้างได้ว่าตรรกะของฉันมีข้อบกพร่องเพราะฉันคิดว่าจะมีเหตุผลที่จะแสดงข้อมูล 2D ในอาร์เรย์ 1D ฉันได้พบกับสถานการณ์หลายอย่างใน C / C ++ แต่ฉันต้องยอมรับว่าจำเป็นต้องทำการคำนวณเช่นนั้นขึ้นอยู่กับภาษา หากอาร์เรย์ดำเนินการคำนวณดัชนีคณิตศาสตร์ทั้งหมดสำหรับไคลเอ็นต์อย่างแท้จริงตลอดเวลาคอมไพเลอร์สามารถแปลงการเข้าถึงอาร์เรย์ M-based ของคุณเป็น 0-based ที่รวบรวมเวลาและซ่อนรายละเอียดการใช้งานทั้งหมดเหล่านี้จากผู้ใช้ ในความเป็นจริงค่าคงที่เวลารวบรวมใด ๆ สามารถใช้ในการดำเนินการชุดเดียวกันแม้ว่าการสร้างดังกล่าวอาจเพียงนำไปสู่รหัสที่เข้าใจไม่ได้

บางทีข้อโต้แย้งที่ดีกว่าคือการลดจำนวนการดำเนินการของดัชนีอาเรย์ในภาษาที่มีอาร์เรย์แบบ 1 จะทำให้การหารจำนวนเต็มดำเนินการโดยใช้ฟังก์ชันเพดาน อย่างไรก็ตามจากมุมมองทางคณิตศาสตร์การหารจำนวนเต็มควรคืนค่า d เหลือ r โดยที่ d และ r เป็นค่าบวก ดังนั้นควรใช้ 0-based arrays เพื่อทำให้คณิตศาสตร์ง่ายขึ้น

ตัวอย่างเช่นหากคุณกำลังสร้างตารางการค้นหาที่มีองค์ประกอบ N ดัชนีที่ใกล้ที่สุดก่อนหน้าค่าปัจจุบันในอาร์เรย์สำหรับค่า x จะเป็น (โดยประมาณจะไม่สนใจค่าที่ผลลัพธ์เป็นจำนวนเต็มก่อนการปัดเศษ):

0-based กับพื้น: floor ((N - 1) * x / xRange)
1-based กับพื้น: floor ((N - 1) * x / xRange) + 1
ใช้ 1 กับเพดาน: ceil ((N - 1) * x / xRange)

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


เป็นเหตุผลที่ดีจนกระทั่งคุณมีภาษาระดับสูงกว่าที่รองรับอาร์เรย์หลายมิติ

1

ฉันพนันได้เลยว่าผู้เขียนโปรแกรมรู้สึกหงุดหงิดกับการตอบโต้ที่ไม่เหมาะสมของอาเรย์ที่มีพื้นฐานจาก 0 ต่อวันและกำลังพิจารณาวิธีการอธิบายอาร์เรย์ที่ใช้งานง่ายขึ้น ฉันคิดว่ามันเป็นเรื่องน่าขันที่ในขณะที่มนุษย์เราใช้เวลามากมายกับ "คลาส" เพื่อที่เราจะได้อธิบายสิ่งต่าง ๆ ในแบบที่เป็นมนุษย์มากกว่าในรหัสของเรา แต่เมื่อมองที่ 0 vs 1 อาร์เรย์เราดูเหมือนจะแขวนอยู่ ตรรกะของมันคนเดียว

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

array(1 => 'January', 'February', 'March');

ให้ 1 อาร์เรย์ตามคำขอของเรา

ทำไมไม่มีบรรทัดฐาน:

array('January', 'February', 'March');

และข้อยกเว้นคือ:

array(0 => 'Value for scenario where 0 *has* to be used as the key',
      'value2', 'value3');

ในกรณีของการพูด PHP การเดิมพันของฉันคือ 80% ของเวลาที่มี 1 อาร์เรย์ที่ใช้ไวยากรณ์เริ่มต้นจะช่วยลดข้อผิดพลาดทางตรรกะในกรณีการใช้งานจริงหรืออย่างน้อยก็ไม่ทำให้เกิดค่าเฉลี่ยมากขึ้นในขณะเดียวกัน coder ง่ายต่อการสร้างรหัสที่ใช้งานได้เร็วขึ้น โปรดจำไว้ว่าฉันสมมติว่ายังคงมีตัวเลือกอาร์เรย์ (0 => 'ค่า') สำหรับเวลาที่ต้องการ แต่ยังสมมติว่าเวลาส่วนใหญ่เป็นประโยชน์ที่จะมีคำอธิบายใกล้เคียงกับโลกแห่งความเป็นจริง

สิ่งนี้ไม่ได้ส่งเสียงร้องขอเมื่อมองจากมุมมองนั้นมากเกินไป เมื่อเข้าใกล้อินเทอร์เฟซไม่ว่าจะเป็นระบบปฏิบัติการหรือภาษาสำหรับโปรแกรมเมอร์ยิ่งใกล้ชิดกับความคิดของมนุษย์และนิสัยที่เราออกแบบมามากความสุขในกรณีส่วนใหญ่เราจะเป็นและความเข้าใจผิดที่น้อยลงระหว่างมนุษย์และคอมพิวเตอร์ ข้อบกพร่อง) และการผลิตที่เร็วขึ้น ฯลฯ เราจะมี หาก 80% ของเวลาในโลกแห่งความจริงฉันอธิบายสิ่งต่าง ๆ ด้วย 1 เมื่อทำรายการหรือนับจากนั้นคอมพิวเตอร์ควรตีความความหมายของฉันให้เป็นวิธีที่เข้าใจข้อมูลหรือเปลี่ยนแปลงเล็กน้อยจากวิธีปกติในการอธิบายสิ่งที่เป็นไปได้ ในระยะสั้นยิ่งเราสามารถจำลองโลกแห่งความเป็นจริงที่มีคุณภาพดีกว่าสิ่งที่เป็นนามธรรม ดังนั้นสิ่งที่เขาต้องการคือไม่ได้โง่เพราะนั่นคือเป้าหมายสูงสุดและจะเป็นหลักฐานของความต้องการที่เป็นนามธรรมมากขึ้น ในที่สุดคอมพิวเตอร์ก็ยังสามารถมองเห็นว่ามันเป็นการใช้งานพิเศษของอาร์เรย์แบบ 0 ฉันไม่แคร์ว่าคอมพิวเตอร์จะตีความได้อย่างไรตราบใดที่มันเป็นวิธีที่ง่ายและเข้าใจง่ายขึ้นสำหรับฉันในการอธิบายสิ่งที่ฉันต้องการด้วยข้อบกพร่องน้อยลงเมื่อเวลาผ่านไป

นั่นคือสองเซ็นต์ของฉัน ฉันสงสัยอย่างจริงจังในสิ่งที่เขาพูดหรือสิ่งที่ถูกตีความว่าเป็นสิ่งที่เขาหมายถึง สิ่งที่เขาอาจหมายถึงคือ "ฉันเกลียดที่จะบอกวิธีการใช้คอมพิวเตอร์ให้เข้าใจง่ายกว่าสิ่งที่ฉันต้องการ" :) พวกเราทุกคนไม่เหรอ? ฮ่า ๆ.


1

เป็นไปได้ถ้าคุณระมัดระวังในขณะที่เขียนรหัส "ของคุณเอง" คุณสามารถสมมติว่าดัชนีของคุณเริ่มต้นจาก n สำหรับทุก n> = 0 และโปรแกรมตาม

เกี่ยวกับมาตรฐาน Borealid มีอาร์กิวเมนต์ที่ดี

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