'<' versus '! =' sebagai kondisi dalam loop 'untuk'?

31

Katakanlah Anda memiliki forloop berikut *:

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

yang secara umum dapat juga ditulis sebagai:

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

Hasil akhirnya sama, jadi apakah ada argumen nyata untuk menggunakan satu di atas yang lain? Secara pribadi saya menggunakan yang pertama kalau i- kalau karena alasan tertentu rusak dan melewatkan nilai 10.

* Maafkan penggunaan angka ajaib, tapi itu hanya sebuah contoh.

Gablin
sumber
18
Solusi terbaik untuk keduanya adalah dengan menggunakan operator panah:int i = 10; while(i --> 0) { /* stuff */ }
corsiKa
@ glowcoder operator panah adalah favorit saya
Carson Myers
3
@ glowcoder, bagus tetapi melintasi dari belakang. Anda mungkin tidak selalu menginginkan itu.
2
@SuperElectric, ia sepertiwhile ((i--) > 0)
Kristopher Johnson
17
Ada (mungkin apokrip) cerita tentang kecelakaan industri yang disebabkan oleh pengujian loop sementara untuk input sensor! = MAX_TEMP. Sayangnya suatu hari input sensor berubah dari kurang dari MAX_TEMP menjadi lebih dari MAX_TEMP tanpa setiap melewati MAX_TEMP. Prosesnya terlalu panas tanpa terdeteksi, dan terjadi kebakaran. Terkadang ada perbedaan antara! = Dan <.
Charles E. Grant

Jawaban:

59

Alasan untuk memilih satu atau yang lain adalah karena niat dan sebagai akibatnya, ini meningkatkan keterbacaan .

Maksud: loop harus berjalan selama ilebih kecil dari 10, tidak selama itidak sama dengan 10. Meskipun yang terakhir mungkin sama dalam kasus khusus ini, bukan itu yang Anda maksud, jadi seharusnya tidak ditulis seperti itu.

Keterbacaan: hasil dari menuliskan apa yang Anda maksud adalah bahwa hal itu juga lebih mudah dipahami. Misalnya, jika Anda menggunakan i != 10, seseorang yang membaca kode mungkin bertanya-tanya apakah di dalam loop ada beberapa cara yang ibisa menjadi lebih besar dari 10 dan bahwa loop harus berlanjut (btw: itu gaya buruk untuk mengacaukan iterator di tempat lain daripada di kepala yang forpernyataan-, tapi itu tidak berarti orang tidak melakukannya dan sebagai hasilnya pengelola berharap).

Deckard
sumber
3
Tidak, loop tidak boleh berjalan selama i"lebih kecil dari 10", itu harus berjalan selama (dalam hal ini) batas atas tidak tercapai. Ketika menggunakan alasan kisaran C ++ interval / iterator, itu jauh lebih logis untuk mengekspresikan hal ini !=daripada <.
Konrad Rudolph
14
@Konrad saya tidak setuju dengan itu sama sekali. Poin utamanya bukan untuk menjadi dogmatis, melainkan memilih konstruk yang paling mengekspresikan maksud dari kondisi tersebut. Jadi, jika Anda memilih untuk mengulang sesuatu mulai dari 0 dan bergerak ke atas, maka <sebenarnya ungkapkan dengan tepat.
Deckard
6
@ Konrad, Anda tidak mengerti intinya. Operator kenaikan di loop ini membuatnya jelas bahwa kondisi loop adalah batas atas, bukan perbandingan identitas. Jika Anda mengurangi, itu akan menjadi batas bawah. Jika Anda mengulangi koleksi yang tidak dipesan, maka identitas mungkin adalah kondisi yang tepat.
Alex Feinman
3
@Alex pertambahan itu bukan poin saya. Selang bahkan tidak perlu memiliki perintah. Itu hanya berulang ... yah, sesuatu sampai mencapai akhir. Misalnya, elemen dalam kumpulan hash. Dalam C ++, ini dilakukan dengan menggunakan iterator dan forloop. Menggunakan di <sini akan konyol. Deckard juga tampaknya mengakui hal itu. Saya sangat setuju dengan komentarnya dalam menanggapi komentar saya.
Konrad Rudolph
1
Catatan, jika Anda menggunakan rotary buffer dengan chase pointer, Anda HARUS menggunakan!=
SF.
48

The <Pola umumnya digunakan bahkan jika kenaikan terjadi tidak menjadi 1 persis.

Hal ini memungkinkan satu cara umum untuk melakukan loop terlepas dari bagaimana itu sebenarnya dilakukan.


sumber
6
Ini juga memungkinkan iuntuk dimodifikasi di dalam loop sepenuhnya dengan aman jika perlu.
Matthew Scharley
1
atau jika 'i' dimodifikasi sepenuhnya tidak aman ... Tim lain memiliki masalah server yang aneh. Itu terus melaporkan penggunaan CPU 100% dan pasti ada masalah dengan server atau sistem pemantauan, kan? Tidak, saya menemukan kondisi loop yang ditulis oleh 'programmer senior ahli' dengan masalah yang sama yang sedang kita bicarakan.
jqa
12
Kecuali bahwa tidak semua C ++ untuk loop dapat digunakan <, seperti iterator atas suatu urutan, dan karenanya !=akan menjadi satu cara umum untuk melakukan loop di C ++.
David Thornley
@SnOrfus: Saya tidak terlalu menguraikan komentar itu. Secara umum, Anda tidak akan mendapatkan pengecualian karena menambahkan iterator terlalu banyak (walaupun ada situasi yang lebih spesifik di mana Anda akan melakukannya).
David Thornley
Yah, saya mengikuti <polanya karena membutuhkan satu ketukan alih-alih dua dengan >polanya, dan empat dengan !=. Ini masalah efisiensi mirip OCD.
Spoike
21

Dalam C ++ rekomendasi oleh Scott Myers di More Effective C ++ (item 6) selalu menggunakan yang kedua kecuali Anda memiliki alasan untuk tidak melakukannya karena itu berarti Anda memiliki sintaks yang sama untuk indeks iterator dan integer sehingga Anda dapat bertukar mulus antara intdan iterator tanpa ada perubahan sintaksis.

Dalam bahasa lain ini tidak berlaku jadi saya kira <mungkin lebih disukai karena poin Thorbjørn Ravn Andersen.

Ngomong-ngomong, hari lain saya mendiskusikan hal ini dengan pengembang lain dan dia mengatakan alasan untuk memilih <lebih !=adalah karena isecara tidak sengaja akan bertambah lebih dari satu, dan itu mungkin menyebabkan kondisi istirahat tidak terpenuhi; itu IMO banyak omong kosong.


sumber
17
"load of nonsense" ... sampai hari ketika Anda secara tidak sengaja memiliki i ++ ekstra dalam tubuh loop.
2
+1, terutama untuk “memuat omong kosong”, karena memang demikian. Jika badan loop secara tidak sengaja menambah penghitung, Anda memiliki masalah yang jauh lebih besar.
Konrad Rudolph
12
@ B Tyler, kita hanya manusia, dan kesalahan besar telah terjadi sebelumnya. Pertimbangkan <untuk menjadi pemrograman yang lebih defensif daripada !=itu ...
2
@ Thorbjørn Ravn Andersen - Saya tidak mengatakan bahwa saya tidak setuju dengan Anda, saya setuju; <lebih defensif dan kolega saya benci jika saya menulis !=jadi saya tidak. Namun, ada kasus !=di C ++ dan itulah yang saya presentasikan.
9
Salah satu skenario di mana seseorang dapat berakhir dengan tambahan yang tidak disengaja i++adalah ketika mengubah loop sementara menjadi loop untuk. Tidak yakin bahwa memiliki setengah jumlah iterasi lebih baik daripada loop tak terbatas (dekat); yang terakhir mungkin akan membuat bug lebih jelas.
ak2
12

Menggunakan (i <10) menurut saya merupakan praktik yang lebih aman. Ini menangkap jumlah maksimum kasus berhenti potensial - semua yang lebih besar dari atau sama dengan 10. Bandingkan ini dengan kasus lainnya (i! = 10); hanya menangkap satu kemungkinan berhenti - ketika saya tepat 10.

Produk sampingannya adalah ini meningkatkan keterbacaan. Selain itu, jika kenaikannya menjadi sesuatu yang lain 1, itu dapat membantu meminimalkan kemungkinan masalah jika kita membuat kesalahan saat menulis kasus berhenti.

Mempertimbangkan:

1) for (i = 1; i < 14; i+=2)

2) for (i = 1; i != 14; i+=2)

Meskipun kedua kasus kemungkinan cacat / salah, yang kedua cenderung LEBIH salah karena tidak akan berhenti. Kasus pertama akan berhenti, dan ada kemungkinan lebih tinggi untuk berhenti di tempat yang tepat, meskipun 14 mungkin angka yang salah (15 mungkin akan lebih baik).

Sparky
sumber
Kasus pertama mungkin benar! Jika Anda ingin mengulangi semua bilangan asli kurang dari 14, maka tidak ada cara yang lebih baik untuk mengekspresikannya - menghitung batas atas yang "tepat" (13) akan benar-benar bodoh.
maaartinus
9

Inilah jawaban lain yang tampaknya belum ada yang muncul. forloop harus digunakan ketika Anda harus mengulangi urutan . Menggunakan !=adalah metode yang paling ringkas menyatakan kondisi terminating untuk loop. Namun, menggunakan operator yang tidak terlalu membatasi adalah idiom pemrograman defensif yang sangat umum. Untuk bilangan bulat tidak masalah - itu hanya pilihan pribadi tanpa contoh yang lebih spesifik. Looping koleksi dengan iterator yang ingin Anda gunakan !=untuk alasan yang telah dinyatakan orang lain. Jika Anda mempertimbangkan urutan floatatau double, maka Anda ingin menghindari !=di semua biaya.

Apa yang ingin saya tunjukkan adalah yang fordigunakan ketika Anda perlu mengulangi urutan. Urutan yang dihasilkan memiliki titik awal, interval, dan kondisi terminating. Ini secara ringkas ditentukan dalam forpernyataan. Jika Anda menemukan diri Anda sendiri (1) tidak termasuk bagian langkah foratau (2) menentukan sesuatu seperti truekondisi penjaga, maka Anda tidak boleh menggunakan forloop!

The whileloop digunakan untuk melanjutkan proses sementara kondisi tertentu terpenuhi. Jika Anda tidak memproses urutan, maka Anda mungkin ingin whilelingkaran sebagai gantinya. Argumen kondisi penjaga serupa di sini, tetapi keputusan antara a whiledan forloop harus sangat sadar. The whileloop kurang dihargai di kalangan C ++ IMO.

Jika Anda sedang memproses koleksi item (penggunaan yang sangat umum for-loop), maka Anda benar-benar harus menggunakan metode yang lebih khusus. Sayangnya, std::for_eachcukup menyakitkan di C ++ karena sejumlah alasan. Dalam banyak kasus memisahkan tubuh dari forloop dalam fungsi yang berdiri bebas (sementara agak menyakitkan) menghasilkan solusi yang jauh lebih bersih. Ketika bekerja dengan koleksi, mempertimbangkan std::for_each, std::transformatau std::accumulate. Implementasi banyak algoritma menjadi ringkas dan jernih ketika diekspresikan dengan cara ini. Belum lagi bahwa mengisolasi tubuh loop ke dalam fungsi / metode terpisah memaksa Anda untuk berkonsentrasi pada algoritma, persyaratan inputnya, dan hasil.

Jika Anda menggunakan Java, Python, Ruby, atau bahkan C ++ 0x , maka Anda harus menggunakan koleksi yang tepat foreach lingkaran. Ketika kompiler C ++ mengimplementasikan fitur ini, sejumlah forloop akan hilang sebagaimana jenis diskusi ini.

D.Shawley
sumber
2
"Namun, menggunakan operator yang tidak terlalu membatasi adalah idiom pemrograman defensif yang sangat umum." Ini merangkum lebih atau kurang.
James P.
1 untuk discussin perbedaan maksud dengan dibandingkan dengan while, for, dan foreach(atau equivilant bahasa)
Kevin Peno
Sebenarnya, yang whileloop digunakan untuk melanjutkan proses sampai kondisi tertentu tidak terpenuhi
Alnitak
@Alnitak - Doh ... Saya akan memperbaikinya :)
D.Shawley
Saya bingung dengan dua kemungkinan arti "kurang restriktif": ini bisa merujuk pada operator bersikap lunak dalam nilai yang dilaluinya ( <) atau operator bersikap lunak dalam nilai yang gagal ( !=).
Mateen Ulhaq
4

Menggunakan "kurang dari" adalah (biasanya) benar secara semantik, maksud Anda benar-benar menghitung sampai itidak kurang dari 10, jadi "kurang dari" menyampaikan niat Anda dengan jelas. Menggunakan "tidak sama" jelas bekerja dalam kasus panggilan sebenarnya, tetapi menyampaikan makna yang sedikit berbeda. Dalam beberapa kasus ini mungkin yang Anda butuhkan tetapi dalam pengalaman saya ini tidak pernah terjadi. Jika Anda benar-benar memiliki kasus di mana imungkin lebih atau kurang dari 10 tetapi Anda ingin tetap mengulang sampai sama dengan 10, maka kode itu benar-benar perlu dikomentari dengan sangat jelas, dan mungkin bisa lebih baik ditulis dengan beberapa konstruksi lain, seperti sebagai whileloop mungkin.

Steve
sumber
2

Salah satu alasan mengapa saya lebih menyukai less thana not equalsadalah untuk bertindak sebagai penjaga. Dalam beberapa keadaan terbatas (pemrograman buruk atau sanitasi) not equalsdapat dilewati sedangkan less thanmasih akan berlaku.

Berikut adalah satu contoh di mana kurangnya pemeriksaan sanitasi telah menyebabkan hasil yang aneh: http://www.michaeleisen.org/blog/?p=358

James P.
sumber
0

Berikut adalah salah satu alasan mengapa Anda lebih suka menggunakan <daripada !=. Jika Anda menggunakan bahasa yang memiliki cakupan variabel global, apa yang terjadi jika kode lain dimodifikasi i? Jika Anda menggunakan <daripada !=, yang terburuk yang terjadi adalah bahwa iterasi selesai lebih cepat: mungkin beberapa peningkatan kode lainnya secara itidak sengaja, dan Anda melewatkan beberapa iterasi dalam for loop. Tetapi apa yang terjadi jika Anda mengulangi 0 hingga 10, dan loop mencapai 9, dan beberapa penambahan untaian yang ditulis buruk ikarena beberapa alasan aneh. Kemudian loop Anda menyelesaikan iterasi dan kenaikan isehingga nilainya sekarang 11. Karena loop telah melewati kondisi keluar ( itidak pernah sama dengan 10) sekarang akan loop berulang.

Itu tidak harus harus terutama jenis logika threading-dan-global-variabel yang menyebabkan ini. Mungkin saja Anda sedang menulis loop yang perlu mundur. Jika Anda bermutasi idi dalam loop dan Anda mengacaukan logika Anda, memilikinya sehingga memiliki batas atas daripada !=kecil cenderung meninggalkan Anda dalam loop tak terbatas.

Tom Morris
sumber
3
Tentu saja, ini sepertinya argumen yang sempurna untuk setiap loop dan gaya pemrograman yang lebih fungsional secara umum. Tapi, mengapa Anda ingin melakukan itu ketika variabel yang bisa berubah jauh lebih menyenangkan ?!
Tom Morris
3
Saya tidak berpikir itu alasan yang sangat bagus. Apa pun cara Anda mendapatkan bug yang perlu ditemukan dan diperbaiki, tetapi loop tak terbatas cenderung membuat bug lebih jelas.
ak2
0

Di dunia tertanam, terutama di lingkungan yang bising, Anda tidak dapat mengandalkan RAM yang seharusnya berlaku sebagaimana mestinya. Dalam kondisi bersyarat (untuk, sementara, jika) di mana Anda membandingkan menggunakan '==' atau '! =' Anda selalu menghadapi risiko variabel Anda melewatkan nilai penting yang mengakhiri loop - ini dapat memiliki konsekuensi yang berbahaya - Mars Lander konsekuensi tingkat. Menggunakan '<' atau '>' dalam kondisi tersebut memberikan tingkat keamanan ekstra untuk menangkap 'tidak dikenal yang tidak dikenal'.

Dalam contoh asli, jika secara itidak sengaja terlempar ke nilai yang jauh lebih besar dari 10, perbandingan '<' akan menangkap kesalahan segera dan keluar dari loop, tetapi '! =' Akan terus menghitung sampai idibungkus melewati 0 dan kembali ke 10.

oosterwal
sumber
2
Variasi terkait lainnya ada dengan kode seperti di for (i=4; i<length; i++) csum+=dat[i];mana paket yang sah akan selalu memiliki lengthsetidaknya empat, tetapi orang mungkin menerima paket yang rusak. Jika jenis paket yang berbeda memiliki persyaratan panjang yang berbeda, orang mungkin ingin memvalidasi panjang sebagai bagian dari pemrosesan yang berbeda untuk setiap jenis paket, tetapi memvalidasi checksum sebagai bagian dari logika umum terlebih dahulu. Jika paket kurang panjang diterima, tidak masalah apakah perhitungan checksum melaporkan "baik" atau "buruk", tetapi tidak boleh macet.
supercat