มีเหตุผลทางเทคนิคหรือไม่ที่จะใช้> (<) แทนที่จะเป็น! = เมื่อเพิ่มขึ้นทีละ 1 ในลูป 'for'?


198

ฉันแทบจะไม่เห็นforลูปแบบนี้:

for (int i = 0; 5 != i; ++i)
{}

มีเหตุผลทางเทคนิคที่จะใช้>หรือ<แทนที่จะ!=เพิ่มทีละ 1 เมื่อforวนซ้ำหรือไม่? หรือนี่เป็นแบบแผนมากกว่ากัน?


104
มันทำให้อ่านได้ง่ายขึ้นและถ้าคุณเปลี่ยนi++เป็นi+=2(เช่น) มันจะทำงานเป็นเวลานานมาก (หรืออาจตลอดไป) ตอนนี้เนื่องจากโดยทั่วไปคุณใช้<สำหรับกรณีเหล่านั้นที่คุณเพิ่มตัววนซ้ำมากกว่า 1 คุณจึงอาจใช้<สำหรับกรณีที่คุณเพิ่มขึ้นด้วย 1 (เพื่อความมั่นคง)
barak manos

112
5 != iเป็นสิ่งที่ชั่วร้าย
Slava

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

40
@OleTange หากคุณกำลังทำซ้ำโดยใช้ตัวเลขลอยจุดที่คุณกำลังเมาแล้ว ..
CaptainCodeman

29
@Slava ... ไม่ใช่ความชั่วร้าย คุณได้เห็นได้ชัดว่าไม่เคยได้รับบิตใน C โดยการพิมพ์ผิดง่าย ๆ ทำให้ความแตกต่างระหว่างและif ( i == 5 ) ... if ( i = 5 )สิ่งที่แตกต่างกันมาก การย้อนกลับตัวถูกดำเนินการช่วยป้องกันปัญหา: เนื่องจากตัวอักษรไม่lvalsได้ถูกกำหนดไว้ไม่สามารถกำหนดได้ @Zingam: การเขียนโปรแกรมการป้องกันที่ดี!
Nicholas Carey

คำตอบ:


303
while (time != 6:30pm) {
    Work();
}

มันคือ 18:31 น. ... ห่ามตอนนี้โอกาสต่อไปของฉันที่จะกลับบ้านคือพรุ่งนี้! :)

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


25
บางครั้งการพยายาม "ลดความเสี่ยง" ก็จบลงด้วยการซ่อนบั๊ก หากการออกแบบของลูปควรตัดเวลา 18:30 น. จากการลื่นไถลโดยไม่มีใครสังเกตเห็นการใช้<จะซ่อนข้อบกพร่อง แต่การใช้!=จะทำให้เห็นได้ชัดว่ามีปัญหา
Adrian McCarthy

27
@AdrianMcCarthy: ไม่จำเป็นต้อง; มันก็สามารถนำข้อผิดพลาดที่สองทำให้การวินิจฉัยยาก
Lightness Races ในวงโคจร

4
@AdrianMcCarthy: นั่นคือจุดของฉัน คุณสามารถมีบางกรณีที่คุณยังไม่ได้เห็น)
Lightness Races ใน Orbit

6
การทำซ้ำเป็นตัวอย่างที่สมบูรณ์แบบของ @AdrianMcCarthy point ที่ฉันคิด การเปรียบเทียบที่แนะนำสำหรับพวกเขาคือ! = แทน <
Taekahn

5
ฉันเห็นข้อผิดพลาดนี้ (และทำให้การดีบัก 3 เดือน) - แต่คำตอบนี้หายไปอย่างสิ้นเชิง ในตัวอย่างที่กำหนดเป็นไปไม่ได้ที่จะพลาดเงื่อนไขสิ้นสุด คุณสามารถพูดได้ว่าการเลือกอย่างมีสติ = มากกว่า <คือการยืนยันความจริงนี้ การลดความเสี่ยงที่ไม่มีอยู่อย่างแน่นอนคือกลิ่นรหัสสำหรับฉัน (ต้องบอกว่าคุณอย่างเท่าเทียมกันสามารถยืนยันว่า <เป็นสำนวนที่ว่าเป็นสิ่งที่ดีมีเหตุผลที่จะใช้มันเป็นใด ๆ .)
เอียน Goldby

95

ไม่มีเหตุผลทางเทคนิค แต่มีการลดความเสี่ยงการบำรุงรักษาและความเข้าใจโค้ด

<หรือ>มีข้อ จำกัด ที่แข็งแกร่งกว่า!=และบรรลุวัตถุประสงค์เดียวกันที่แน่นอนในกรณีส่วนใหญ่ (ฉันอาจพูดในกรณีที่ปฏิบัติได้ทั้งหมด)

มีคำถามซ้ำที่นี่ ; และเป็นหนึ่งที่น่าสนใจคำตอบ


6
มีบางประเภทเช่นตัววนซ้ำที่ไม่รองรับ <หรือ> แต่ให้การสนับสนุน == และ! =
Simon B

1
มันเป็นห่วงกับint ไม่มีตัววนซ้ำ
Ely

7
"แต่มีการลดความเสี่ยงการบำรุงรักษาและความเข้าใจโค้ดได้ดีขึ้น" ทั้งหมดนี้เป็นเหตุผลทางเทคนิคที่ถูกต้อง :-)
TJ Crowder

@TJCrowder มันมีเหตุผลแบบไหนเมื่อคุณต้องการพูดคุยเกี่ยวกับผลลัพธ์ที่ได้จากเครื่อง?
Brilliand

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

85

ใช่มีเหตุผล ถ้าคุณเขียน (สร้างดัชนีแบบเก่าแบบธรรมดา) สำหรับลูปแบบนี้

for (int i = a; i < b; ++i){}

จากนั้นจะทำงานตามที่คาดไว้สำหรับค่าใด ๆ ของaและb(เช่นทำซ้ำเป็นศูนย์เมื่อa > bแทนที่ไม่มีที่สิ้นสุดหากคุณเคยใช้i == b; )

ในทางกลับกันสำหรับตัววนซ้ำที่คุณเขียน

for (auto it = begin; it != end; ++it) 

เนื่องจากตัววนซ้ำใด ๆ ควรนำมาใช้operator!=แต่ไม่ใช่สำหรับตัววนซ้ำทุกตัวจึงเป็นไปได้ที่จะให้operator<มันเป็นไปได้ที่จะให้

นอกจากนี้ยังขึ้นอยู่กับช่วงสำหรับลูป

for (auto e : v)

ไม่ใช่แค่น้ำตาลแฟนซี แต่สามารถลดโอกาสในการเขียนรหัสผิด


4
ตัวอย่างที่ดี ฉันจะสนใจในกรณีสำหรับแทน!= <โดยส่วนตัวฉันไม่เคยใช้สิ่งที่ฉันคิด
Ely

@Elyasin ฉันไม่พบสิ่งที่ดีและฉันไม่ต้องการที่จะโพสต์ที่ไม่ดี: ลองนึกภาพคุณมีภาชนะบรรจุแบบวงกลมบางอย่างที่คุณเพิ่มโดย + x modulo ขนาดของภาชนะ N และคุณต้องการที่จะหยุดวงเมื่อคุณ เข้าถึงดัชนีที่แน่นอน .... นั่นเป็นตัวอย่างที่ไม่ดีจริงๆ บางทีฉันอาจจะหาคนที่ดีกว่า
idclev 463035818

ลูปเดียวกันกับ! = แทน <ทำให้ชัดเจนว่าข้อดีของ <(หรือ>) อยู่ในบริบทนี้ for (int i=a; i != b; i++) {}
Paul Smith

10
การใช้สิ่งอื่นนอกเหนือจากตัว!=วนซ้ำนั้นค่อนข้างอันตรายเพราะบางครั้งตัววนซ้ำอาจน้อยกว่าการเปรียบเทียบ (เช่นตัวชี้) แต่การเปรียบเทียบนั้นไม่มีความหมาย (เป็นรายการที่เชื่อมโยง)
Zan Lynx

1
เรียนรู้การใช้พื้นที่สีขาวอย่างจริงจัง
Miles Rout

70

คุณสามารถมีสิ่งที่ชอบ

for(int i = 0; i<5; ++i){
    ...
    if(...) i++;
    ...
}

หากตัวแปรลูปของคุณถูกเขียนโดยโค้ดภายในi!=5อาจไม่ทำให้เกิดลูปนั้น วิธีนี้ปลอดภัยกว่าในการตรวจสอบความไม่เท่าเทียมกัน

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


6
ด้านในประเภท "if (condition) จากนั้น i ++" เป็นสิ่งแรกที่ฉันคิด
WernerCD

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

1
@msouth ผมอย่างเห็น แต่มุมมองของฉันเป็นอัตนัยค่อนข้าง;)
d Johan

3
@msouth ฉันชอบเมื่อพฤติกรรมการผลิตที่ไม่คาดคิดทำให้เกิดบั๊กของฉันใช่ไหม? :-)
deworde

3
@msouth Look สิ่งที่เราต้องทำคือไม่เคยทำผิดพลาดเลยและทุกอย่างจะดี Simples
deworde

48

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

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


2
ตัวอย่างที่ดีหนึ่งที่สนับสนุนสิ่งนี้: หน่วยความจำมีความเสี่ยงต่อการพลิกบิต ( SEU ) ที่เกิดจากการแผ่รังสี เมื่อใช้intสำหรับi != 15เงื่อนไขตัวนับจะทำงานเป็นเวลานานหากสิ่งใดที่อยู่เหนือบิตที่สี่ถูกพลิกโดยเฉพาะอย่างยิ่งกับเครื่องที่sizeof(int)64 64 SEUs เป็นปัญหาที่แท้จริงที่ระดับความสูงหรือในอวกาศที่สูงขึ้น (เนื่องจากรังสีที่สูงขึ้น) หรือในซูเปอร์คอมพิวเตอร์ขนาดใหญ่ (เพราะมีหน่วยความจำอยู่มาก)
Josh Sanford

2
การคิดว่าโปรแกรมของคุณจะทำงานได้ดีเมื่อรังสีเปลี่ยนหน่วยความจำของคุณเป็นวิธีการมองโลกในแง่ดีและวิธีคิดต่อต้านมะเร็งที่ดี ... ความคิดเห็นดี! :)
Luis Colorado

37

ฉันจะยืนยันว่าการแสดงออกเช่น

for ( int i = 0 ; i < 100 ; ++i )
{
  ...
}

เป็นมากกว่าการแสดงออกของเจตนากว่า

for ( int i = 0 ; i != 100 ; ++i )
{
  ...
}

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


13
"ฉันต้องการให้วนรอบนี้ทำงานได้สูงสุด 5 ครั้ง" กับ "ฉันต้องการเรียกใช้วนซ้ำนี้บ่อยครั้งเท่าที่จำเป็นยกเว้น 5 เท่าที่ฉันไม่ต้องการ"
อธิการ

+1 สำหรับการพูดถึงขอบเขตบนของช่วง ฉันคิดว่านี่เป็นสิ่งสำคัญสำหรับช่วงตัวเลข ! = ไม่ได้กำหนดช่วงที่เหมาะสมในการวนรอบเช่นนี้
element11

22

การทำซ้ำเป็นกรณีที่สำคัญเมื่อคุณใช้!=สัญกรณ์บ่อยที่สุด:

for(auto it = vector.begin(); it != vector.end(); ++it) {
 // do stuff
}

ได้รับ: ในทางปฏิบัติฉันจะเขียนแบบเดียวกันโดยอาศัยrange-for:

for(auto & item : vector) {
 // do stuff
}

แต่ยังคงเป็นจุดหนึ่งตามปกติเปรียบเทียบ iterators ใช้หรือ==!=


2
Iterators มักจะมีเพียงความคิดของความเสมอภาค / ความไม่เท่าเทียมกันและไม่ใช่การสั่งซื้อนั่นคือเหตุผลที่คุณใช้!=สำหรับพวกเขา ตัวอย่างเช่นใน a std::vectorคุณสามารถใช้<เป็นตัววนซ้ำถูกผูกติดกับดัชนี / ตัวชี้ แต่ใน a std::setไม่มีการเรียงลำดับที่เข้มงวดเช่นนั้นขณะที่มันเดินต้นไม้ไบนารี
Martin C.

19

เงื่อนไขลูปเป็นค่าคงที่ลูปที่ถูกบังคับ

สมมติว่าคุณไม่ได้ดูที่เนื้อความของลูป:

for (int i = 0; i != 5; ++i)
{
  // ?
}

ในกรณีนี้คุณจะรู้ว่าจุดเริ่มต้นของการย้ำห่วงว่าไม่เท่ากับi5

for (int i = 0; i < 5; ++i)
{
  // ?
}

ในกรณีนี้คุณรู้ตอนเริ่มวนซ้ำที่iน้อยกว่า5จะน้อยกว่า

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

คุณรู้เพิ่มเติมเกี่ยวกับสถานะของโปรแกรมจากการอ่านโค้ดน้อยลง<กว่าด้วย!=กว่าด้วยและสำหรับซีพียูรุ่นใหม่พวกเขาใช้เวลาเท่ากันโดยไม่แตกต่างกัน

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

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

ใน C ++ คุณสามารถเขียนindexesประเภทที่:

for( const int i : indexes(0, 5) )
{
  // ?
}

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

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



13

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


1
ไม่สิ้นสุดจริงๆ - สำหรับการใช้งานส่วนใหญ่ของ C iจะปิดล้อมเมื่อสูงสุด แต่ใช่นานกว่าที่คุณพร้อมที่จะรอ
usr2564301

12

อย่างที่คุณเห็นจากคำตอบมากมายอื่น ๆ มีเหตุผลที่จะใช้ <แทน! = ซึ่งจะช่วยในกรณีขอบเงื่อนไขเริ่มต้นการปรับเปลี่ยนเคาน์เตอร์วนซ้ำที่ไม่ได้ตั้งใจ ฯลฯ ...

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


12

เป็นแล้วกล่าวว่าโดยเอียน Newson !=คุณไม่สามารถเชื่อถือได้ห่วงมากกว่าตัวแปรลอยและออกด้วย ตัวอย่างเช่น

for (double x=0; x!=1; x+=0.1) {}

จะวนซ้ำตลอดไปจริง ๆ เพราะ 0.1 ไม่สามารถแสดงในจุดลอยตัวได้อย่างแม่นยำดังนั้นตัวนับจะพลาด 1 ด้วย <จึงสิ้นสุดลง

(โปรดทราบว่ามันเป็นพฤติกรรมที่ไม่ได้กำหนดโดยทั่วไปไม่ว่าคุณจะได้รับ 0.9999 ... เป็นหมายเลขที่ยอมรับล่าสุด - ประเภทใดที่ละเมิดสมมติฐานน้อยกว่า - หรือออกแล้วที่ 1.0000000000000001)


10

ฉันใช้คำคุณศัพท์ "เทคนิค" เพื่อหมายถึงพฤติกรรมภาษา / นิสัยใจคอและผลข้างเคียงของคอมไพเลอร์เช่นประสิทธิภาพของโค้ดที่สร้างขึ้น

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

หากคุณกำลังใช้กรณีขอบคุณอาจจะพบว่า!=ต้องสามการดำเนินงาน ( cmp, not, beq) VS สอง ( cmp, blt xtr myo) อีกครั้ง RTM ในกรณีนั้น

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

// highly contrived example
size_t count_chars(char c, const char* str, size_t len) {
    size_t count = 0;
    bool quoted = false;
    const char* p = str;
    while (p != str + len) {
        if (*p == '"') {
            quote = !quote;
            ++p;
        }
        if (*(p++) == c && !quoted)
            ++count;
    }
    return count;
}

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

#include <iostream>
int main() {
    size_t len = 5, step;
    for (size_t i = 0; i != len; ) {
        std::cout << "i = " << i << ", step? " << std::flush;

        std::cin >> step;
        i += step; // here for emphasis, it could go in the for(;;)
    }
}

ลองทำสิ่งนี้และป้อนค่า 1, 2, 10, 999

คุณสามารถป้องกันสิ่งนี้:

#include <iostream>
int main() {
    size_t len = 5, step;
    for (size_t i = 0; i != len; ) {
        std::cout << "i = " << i << ", step? " << std::flush;
        std::cin >> step;
        if (step + i > len)
            std::cout << "too much.\n";
        else
            i += step;
    }
}

แต่สิ่งที่คุณอาจต้องการคือ

#include <iostream>
int main() {
    size_t len = 5, step;
    for (size_t i = 0; i < len; ) {
        std::cout << "i = " << i << ", step? " << std::flush;
        std::cin >> step;
        i += step;
    }
}

นอกจากนี้ยังมีบางสิ่งบางอย่างที่มีอคติต่อการประชุม<เนื่องจากการสั่งซื้อในคอนเทนเนอร์มาตรฐานมักจะอาศัยoperator<เช่นการแฮ็กในคอนเทนเนอร์ STL หลายรายการจะกำหนดความเท่าเทียมกันโดยการพูด

if (lhs < rhs) // T.operator <
    lessthan
else if (rhs < lhs) // T.operator < again
    greaterthan
else
    equal

ถ้าlhsและrhsเป็นคลาสที่ผู้ใช้กำหนดให้เขียนโค้ดนี้เป็น

if (lhs < rhs) // requires T.operator<
    lessthan
else if (lhs > rhs) // requires T.operator>
    greaterthan
else
    equal

ตัวดำเนินการต้องจัดเตรียมฟังก์ชันการเปรียบเทียบสองฟังก์ชัน ดังนั้น<ได้กลายเป็นผู้ประกอบการที่ชื่นชอบ


เพื่อความสะดวกในการอ่าน i + = ขั้นตอนควรอยู่ในส่วนควบคุม ในถ้าคุณจะ step = 0
Mikkel Christiansen

1
ในขณะที่จะช่วยให้สามารถอ่านรหัสที่ดีฉันต้องการเน้นข้อบกพร่องในรุ่นที่ผิดพลาด
kfsone

9

มีหลายวิธีในการเขียนโค้ดประเภทใด ๆ (โดยปกติ) มีเพียงสองวิธีในกรณีนี้ (สามถ้าคุณนับ <= และ> =)

ในกรณีนี้คนต้องการ> และ <เพื่อให้แน่ใจว่าแม้จะมีบางสิ่งที่ไม่คาดคิดเกิดขึ้นในลูป (เช่นข้อผิดพลาด) แต่มันจะไม่วนซ้ำไม่สิ้นสุด (BAD) พิจารณารหัสต่อไปนี้เช่น

for (int i = 1; i != 3; i++) {
    //More Code
    i = 5; //OOPS! MISTAKE!
    //More Code
}

ถ้าเราใช้ (i <3) เราจะปลอดภัยจากการวนซ้ำไม่สิ้นสุดเพราะมันมีข้อ จำกัด ที่ใหญ่กว่า

เป็นตัวเลือกของคุณจริง ๆ ไม่ว่าคุณต้องการความผิดพลาดในโปรแกรมของคุณเพื่อปิดสิ่งทั้งหมดหรือทำงานกับบั๊กที่นั่น

หวังว่านี่จะช่วยได้!


หนึ่งสามารถโต้แย้งว่าวงอนันต์จะเป็นตัวบ่งชี้ที่ดีของความผิดพลาด แต่แล้วอีกคนหนึ่งจะยืนยันว่าฉัน = 5 ไม่จำเป็นต้องเป็นข้อผิดพลาด
njzk2

7

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

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

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

ตัวอย่างเช่นหากคุณกำลังก้าวผ่านสตริง C ธรรมดามันเป็นเรื่องปกติที่จะเขียน:

for (char *p = foo; *p != '\0'; ++p) {
  // do something with *p
}

กว่า

int length = strlen(foo);
for (int i = 0; i < length; ++i) {
  // do something with foo[i]
}

สำหรับสิ่งหนึ่งหากสตริงยาวมากรูปแบบที่สองจะช้าลงเนื่องจากstrlenเป็นอีกรูปแบบหนึ่งที่ส่งผ่านสตริง

ด้วยสตริง C ++ std :: คุณจะใช้ช่วงตามลูปอัลกอริธึมมาตรฐานหรือตัววนซ้ำแม้ว่าความยาวนั้นจะพร้อมใช้งานก็ตาม หากคุณกำลังใช้ตัววนซ้ำการประชุมจะต้องใช้!=มากกว่า<ใน:

for (auto it = foo.begin(); it != foo.end(); ++it) { ... }

ในทำนองเดียวกันการวนซ้ำต้นไม้หรือรายการหรือ deque มักเกี่ยวข้องกับการเฝ้าดูตัวชี้โมฆะหรือแมวมองอื่น ๆ แทนที่จะตรวจสอบว่าดัชนียังคงอยู่ในช่วง


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

7

เหตุผลหนึ่งที่จะไม่ใช้สิ่งก่อสร้างนี้คือตัวเลขจุดลอยตัว !=เป็นการเปรียบเทียบที่อันตรายมากที่จะใช้กับโฟลทเพราะมันจะไม่ค่อยประเมินว่าเป็นจริงแม้ว่าตัวเลขจะดูเหมือนกัน <หรือ>ขจัดความเสี่ยงนี้


หากวนซ้ำของคุณเป็นจำนวนจุดลอยตัวแล้ว!=เมื่อเทียบกับ<ปัญหาของคุณน้อยที่สุด
Lundin

7

มีสองเหตุผลที่เกี่ยวข้องสำหรับการปฏิบัติตามนี้ซึ่งทั้งสองเกี่ยวข้องกับความจริงที่ว่าภาษาการเขียนโปรแกรมคือหลังจากทั้งหมดภาษาที่มนุษย์จะอ่าน (ในหมู่อื่น ๆ )

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

(2) อนุสัญญา ภาษามีวิธีมาตรฐานเฉพาะในการแสดงสถานการณ์บางอย่าง หากคุณไม่ปฏิบัติตามวิธีที่กำหนดไว้ว่าจะพูดอะไรบางอย่างคุณจะยังคงเข้าใจอยู่ แต่ความพยายามในการรับข้อความของคุณจะดีกว่าเนื่องจากการเพิ่มประสิทธิภาพบางอย่างจะไม่ทำงาน ตัวอย่าง:

ไม่ต้องพูดถึงเครื่องบดที่ร้อนจัด เพียงส่องความยาก!

ประโยคแรกคือการแปลตามตัวอักษรของสำนวนเยอรมัน ที่สองคือสำนวนภาษาอังกฤษทั่วไปที่มีคำหลักที่ถูกแทนที่ด้วยคำพ้องความหมาย ผลลัพธ์สามารถเข้าใจได้ แต่ใช้เวลาในการทำความเข้าใจนานกว่านี้:

อย่าตีรอบพุ่มไม้ เพียงอธิบายปัญหา!

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


1
Ausgezeichnet ในทำนองเดียวกันเราจะไม่เขียนการทดสอบว่าi < 17+(36/-3)(แม้ว่าจะเป็นค่าคงที่ที่ใช้ในที่อื่นคุณต้องเขียนชื่อเพื่อความชัดเจน!)
usr2564301

6

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

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

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

for (unsigned i = 42; i != -1; --i)
  ...

โปรดทราบว่าข้อความด้านบนสามารถใช้ได้กับการวนซ้ำทั้งที่ลงชื่อและไม่ได้ลงชื่อในขณะที่เวอร์ชันสัมพันธ์แบ่งประเภทที่ไม่ได้ลงนาม


2

นอกเหนือจากตัวอย่างที่ตัวแปรลูปจะเปลี่ยน (โดยไม่ได้ตั้งใจ) ภายในร่างกายยังมี reasions อื่น ๆ ที่จะใช้ตัวดำเนินการที่เล็กกว่าหรือใหญ่กว่า:

  • การปฏิเสธทำให้รหัสเข้าใจได้ยากขึ้น
  • <หรือ>เป็นเพียงถ่านเดียว แต่!=ทั้งสอง

4
สัญลักษณ์แสดงหัวข้อย่อยที่สองที่ดีที่สุดเป็นเหตุผลที่ไม่สำคัญ รหัสควรถูกต้องและชัดเจน กะทัดรัดมีความสำคัญน้อยกว่ามาก
Jonathan Leffler

@JanathanLeffler ใน TDD REPL ตัวละครพิเศษนั้นคือนาโนวินาทีพิเศษหนึ่งสำหรับการแยกวิเคราะห์และคูณหนึ่งพันล้านครั้งเพื่อค้นหาจุดบกพร่องที่เกิดจากการเขียน5 != $iซึ่งใช้เวลาเพียงหนึ่งวินาทีในชีวิตของฉัน เอ่อ ... ขำขำ
บิชอป

1

นอกเหนือจากบุคคลต่าง ๆ ที่กล่าวถึงว่ามันช่วยลดความเสี่ยงมันยังช่วยลดจำนวนฟังก์ชั่นโอเวอร์โหลดที่จำเป็นในการโต้ตอบกับส่วนประกอบไลบรารีมาตรฐานต่างๆ ตัวอย่างเช่นหากคุณต้องการให้ประเภทของคุณสามารถจัดเก็บในstd::setหรือใช้เป็นคีย์สำหรับstd::mapหรือใช้กับอัลกอริทึมการค้นหาและการเรียงลำดับบางส่วนไลบรารีมาตรฐานมักจะใช้std::lessเพื่อเปรียบเทียบวัตถุเป็นอัลกอริธึมส่วนใหญ่ต้องการลำดับอ่อนแออย่างเข้มงวด . ดังนั้นมันจึงกลายเป็นนิสัยที่ดีในการใช้การ<เปรียบเทียบแทน!=การเปรียบเทียบ (ซึ่งแน่นอนว่าสมเหตุสมผล)


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

0

ไม่มีปัญหาจากเปอร์สเปคทีฟของมุมมอง แต่ตรรกะที่อยู่เบื้องหลังนิพจน์5!=iนั้นไม่ใช่เสียง

ในความคิดของฉันการใช้!=เพื่อกำหนดขอบเขตของ for for loop นั้นไม่ได้มีเหตุผลทางด้านเสียงเพราะ a สำหรับ loop นั้นเพิ่มขึ้นหรือลดลงดัชนีการวนซ้ำดังนั้นการตั้งค่า loop ให้วนซ้ำจนกว่าดัชนีการวนซ้ำจะออกนอกขอบเขต (!=เพื่ออะไร) ไม่ได้เป็น การใช้งานที่เหมาะสม

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


2
ตรรกะดีอย่างสมบูรณ์ เมื่อiเป็น5ทางออกห่วง คุณหมายถึงพูดอย่างอื่นหรือ
Dan Getz

1
หากข้อมูลไม่ได้รับการถ่ายโอนอย่างถูกต้องเนื่องจากความร้อน electro.magnetic จะมีผลต่อโค้ดของคุณตลอดไป ถ้าคุณทำเช่นนี้ในเวิร์กสเตชันปกติหรือเซิร์ฟเวอร์กับ ECC RAM ไม่มีปัญหา (ค่าตรรกะทางเทคนิคหรือทางกายภาพ)
มาร์คุส
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.