มีประโยชน์ใด ๆ ของการใช้ตัวแปรพิเศษนี้ในการทำหมายเหตุประกอบแบบลูปหรือไม่


11

ฉันได้พบคำอธิบายประกอบแบบวนรอบต่อไปนี้ในโครงการขนาดใหญ่ที่ฉันกำลังทำงาน (pseudocode):

var someOtherArray = [];
for (var i = 0, n = array.length; i < n; i++) {
    someOtherArray[i] = modifyObjetFromArray(array[i]);
}

สิ่งที่ทำให้ฉันสนใจคือตัวแปรพิเศษ "n" ฉันไม่เคยเห็น a lop เขียนในลักษณะนี้มาก่อน

เห็นได้ชัดว่าในสถานการณ์นี้ไม่มีเหตุผลว่าทำไมโค้ดนี้ไม่สามารถเขียนได้ด้วยวิธีต่อไปนี้ (ซึ่งฉันคุ้นเคยมาก):

var someOtherArray = [];
for (var i = 0; i < array.length; i++) {
    someOtherArray[i] = modifyObjetFromArray(array[i]);
}

แต่มันทำให้ฉันคิด

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

การลดขนาดอาร์เรย์ภายในลูปไม่สมเหตุสมผลมากนักเนื่องจากเรามีแนวโน้มที่จะได้รับ OutOfBoundsException

มีรูปแบบการออกแบบที่เป็นที่รู้จักซึ่งการเพิ่มความคิดเห็นนี้มีประโยชน์หรือไม่

แก้ไขดัง ที่บันทึกไว้โดย @ Jerry101 เหตุผลคือประสิทธิภาพ นี่คือการเชื่อมโยงในการทดสอบประสิทธิภาพการทำงานที่ผมได้สร้าง: http://jsperf.com/ninforloop ในความเห็นของฉันความแตกต่างนั้นไม่ใหญ่พอเว้นแต่คุณจะทำซ้ำแม้ว่าจะมีชุดใหญ่มาก รหัสที่ฉันคัดลอกมานั้นมีองค์ประกอบมากถึง 20 ชิ้นเท่านั้นดังนั้นฉันจึงคิดว่าการอ่านในกรณีนี้มีค่ามากกว่าการพิจารณาเกี่ยวกับประสิทธิภาพ


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

ฉันเห็นด้วย. เป็นเรื่องดีที่รู้เกี่ยวกับตัวเลือกนี้ แต่ฉันอาจไม่ได้ใช้บ่อยนักเว้นแต่ฉันจะได้รับตัวอย่างที่คล้ายคลึงกับข้อความค้นหา SQL ของคุณ ขอบคุณ
Vlad

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

1
ฉันไม่พบว่าสิ่งนี้จะทำให้การอ่านรหัสยากขึ้นอย่างมีนัยสำคัญ (อย่างจริงจัง, ถ้าใครมีปัญหากับลูปแบบง่าย ๆ , พวกเขาควรกังวล) แต่: 1) ฉันจะประหลาดใจมากถ้าหลังจาก 4 ทศวรรษของภาษา C และ C-descended, ผู้รวบรวมที่จริงจังทุกคนไม่ได้ทำแบบนี้ สำหรับคุณ; และ 2) นี่อาจไม่ใช่จุดที่น่าสนใจ มันดูเหมือนจะไม่คุ้มค่าการทำคนใช้จ่ายเพิ่ม 5 วินาทีแยกมันจนกว่ามันอยู่ภายในห้องสมุด (เช่นการดำเนินงานของโครงสร้างข้อมูล.)
Doval

1
ใน C # /. NET การใช้ตัวแปรnอาจช้ากว่าการใช้ตัวแปรarray.Lengthเนื่องจาก JITter อาจไม่สังเกตเห็นว่าสามารถกำจัดการตรวจสอบขอบเขตของอาร์เรย์ได้
CodesInChaos

คำตอบ:


27

ตัวแปรnช่วยให้มั่นใจว่ารหัสที่สร้างขึ้นไม่สามารถดึงความยาวของอาเรย์สำหรับการวนซ้ำทุกครั้ง

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


3
ไม่เลย ไม่ว่าจะเป็นความยาวของอาเรย์เป็นความจริงทุกย้ำขึ้นไม่เพียง แต่ในภาษา แต่ยังเกี่ยวกับคอมไพเลอร์และตัวเลือกคอมไพเลอร์และสิ่งที่จะทำในวง (ฉันกำลังพูดคุยเกี่ยวกับ C และ C ++)
BЈовић

.. และถึงอย่างนั้นฉันก็จะมอบหมายการมอบหมาย n ให้อยู่เหนือลูปถ้าฉันต้องการจริงๆเนื่องจาก - ตามที่ OP ตั้งข้อสังเกตไว้ - นี่เป็นโครงสร้างที่หายาก (YMMV) ที่จะใช้และมันก็พลาดง่าย / ไม่เข้าใจจาก devs อื่น ๆ รหัส.
cwap

20
ตามที่เขียนไว้แล้วมันจะสโคปnกับลูปซึ่งโดยทั่วไปเป็นสิ่งที่ดี ฉันจะกำหนดไว้ก่อนการวนซ้ำก็ต่อเมื่อฉันต้องการใช้อีกครั้งในภายหลัง
Jerry101

4
@ Jerry101: เห็นได้ชัดว่าตัวอย่างข้อมูลตัวอย่างคือ JavaScript ซึ่งไม่มีสิ่งเช่นขอบเขตลูป ...
Bergi

1
@ BЈовић: ในทางปฏิบัติคอมไพเลอร์ไม่ค่อยจัดการเพื่อพิสูจน์ว่าขนาดอาร์เรย์ไม่แตกต่างกัน (โดยทั่วไปจะพิสูจน์ได้เล็กน้อยหากเวกเตอร์ที่คุณวนซ้ำนั้นเป็นโลคัลของฟังก์ชันและคุณเรียกฟังก์ชัน inlined เฉพาะในลูป)
Matteo Italia

2

การดึงความยาวของอาเรย์อาจเป็น "ราคาแพงกว่า" อย่างง่ายดายจากนั้นการกระทำจริงที่คุณทำซ้ำ

ดังนั้นหากชุดไม่เปลี่ยนแปลงให้ค้นหาความยาวเพียงครั้งเดียว

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


หากบันทึกนับการเปลี่ยนแปลงแล้วอะไร สมมติว่ามี 100 รายการและในขณะที่คุณกำลังประมวลผลรายการ 50 รายการหลังจาก # 25 ถูกแทรก ดังนั้นเมื่อคุณประมวลผลรายการ # 51 ก็เหมือนกับ # 50 ที่คุณดำเนินการแล้วและสิ่งต่าง ๆ ผิดพลาด ดังนั้นปัญหาไม่ใช่การเปลี่ยนแปลงความยาวนั้นมันแพร่หลายมากขึ้น
gnasher729

@ gnasher729 เมื่อคุณได้รับพฤติกรรมที่ไม่สมมาตรมีหลายอย่างที่ผิดพลาดได้ แต่มีสิ่งที่คุณสามารถทำกับสิ่งนั้นได้เช่นการเริ่มต้นและธุรกรรม SQL
Pieter B

2

แนวคิดมาอยู่ในใจว่าความยาวของ "อาเรย์" อาจมีการเปลี่ยนแปลงในระหว่างการดำเนินการลูป แต่เราไม่ต้องการวนซ้ำเกินกว่าขนาดดั้งเดิม แต่ฉันไม่สามารถจินตนาการถึงสถานการณ์ดังกล่าวได้

ผู้คนที่พูดถึงการแสดงอาจถูกต้องว่านี่คือสาเหตุที่เขียนขึ้นมาด้วยวิธีนี้ (คนที่เขียนแบบนั้นอาจไม่ถูกต้องว่ามันมีผลกระทบต่อการแสดงอย่างมีนัยสำคัญ

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

guests = [];
for (i = 0; i < invitations.length; ++i) {
    if (! invitations[i].is_declined()) {
        guests.append(invitations[i].recipient())
    }
}
// maybe some other stuff to modify the guestlist
for (i = 0, n = guests.length; i < n; ++i) {
    if (guests[i].has_plus_one()) {
        guests.append(guests[i].get_plus_one());
    }
}

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

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


Array สามารถแก้ไขได้ด้วยเธรดอื่น สิ่งนี้จะป้องกันไม่ให้คอมไพเลอร์ปรับการดึงความยาวให้เหมาะสม ดังนั้นในคำถามโปรแกรมเมอร์อธิบายอย่างชัดเจนว่าอาเรย์นั้นไม่เปลี่ยนแปลงแม้แต่ในเธรดอื่น ๆ
Basilevs

0

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

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