Haruskah <= dan> = dihindari ketika menggunakan bilangan bulat, seperti dalam loop For? [Tutup]

15

Saya telah menjelaskan kepada siswa saya bahwa pengujian sama dengan tidak dapat diandalkan untuk variabel float, tetapi baik untuk bilangan bulat. Buku teks yang saya gunakan mengatakan bahwa lebih mudah dibaca> dan <daripada> = dan <=. Saya setuju sampai batas tertentu, tetapi dalam lingkaran For? Bukankah lebih jelas untuk memiliki loop menentukan nilai awal dan akhir?

Apakah saya melewatkan sesuatu yang benar oleh penulis buku teks?

Contoh lain dalam tes Range seperti:

jika skor> 89 grade = 'A'
lain jika skor> 79 grade = 'B' ...

Mengapa tidak mengatakan saja: jika skor> = 90?


sumber
11
Sayangnya, karena tidak ada perbedaan obyektif dalam perilaku antara opsi-opsi ini, ini merupakan jajak pendapat tentang apa yang orang anggap lebih intuitif, dan jajak pendapat tidak cocok untuk situs StackExchange seperti ini.
Ixrec
1
Itu tidak masalah. Betulkah.
Auberon
4
Sebenarnya, ini bisa dijawab secara objektif. Bersiap ...
Robert Harvey
9
@Irrec Saya selalu merasa menarik bahwa "praktik terbaik" tidak dianggap sebagai topik yang cocok. Siapa yang tidak ingin meningkatkan, atau membuat kodenya lebih mudah dibaca? Jika orang tidak setuju, kita semua belajar lebih banyak sisi dari masalah ini, dan mungkin ... bahkan ... mengubah pikiran kita! Ugh, itu sulit dikatakan. Robert Harvey mengatakan dia bisa menjawabnya secara objektif. Ini akan menarik.
1
@nocomprende Sebagian besar karena "praktik terbaik" adalah istilah yang sangat kabur dan sering digunakan yang dapat merujuk saran bermanfaat berdasarkan fakta obyektif tentang bahasa tersebut, tetapi sama seringnya merujuk pada "pendapat paling populer" (atau pendapat siapa pun yang menggunakan istilah itu) padahal kenyataannya semua opsi sama-sama valid dan tidak valid. Dalam hal ini, Anda hanya dapat membuat argumen obyektif dengan membatasi jawaban untuk jenis loop tertentu seperti yang dilakukan Robert, dan ketika Anda menunjukkan diri Anda dalam komentar yang tidak sepenuhnya menjawab pertanyaan.
Ixrec

Jawaban:

36

Dalam bahasa pemrograman yang dikeriting keriting dengan array berbasis nol , biasanya untuk menulis forloop seperti ini:

for (int i = 0; i < array.Length, i++) { }

Ini melintasi semua elemen dalam array, dan sejauh ini merupakan kasus yang paling umum. Ini menghindari penggunaan <=atau >=.

Satu-satunya waktu yang harus diubah adalah ketika Anda harus melewati elemen pertama atau terakhir, atau melewatinya ke arah yang berlawanan, atau melewatinya dari titik awal yang berbeda atau ke titik akhir yang berbeda.

Untuk koleksi, dalam bahasa yang mendukung iterator, lebih umum untuk melihat ini:

foreach (var item in list) { }

Yang menghindari perbandingan sepenuhnya.

Jika Anda mencari aturan yang keras dan cepat tentang kapan harus menggunakan <=vs <, tidak ada; gunakan apa yang terbaik mengekspresikan niat Anda. Jika kode Anda perlu mengekspresikan konsep "Kurang dari atau sama dengan 55 mil per jam," maka perlu dikatakan <=, tidak <.

Untuk menjawab pertanyaan Anda tentang rentang nilai, >= 90lebih masuk akal, karena 90 adalah nilai batas aktual, bukan 89.

Robert Harvey
sumber
9
Itu tidak menghindari perbandingan sepenuhnya, itu hanya menyapu mereka di bawah karpet dengan memindahkan mereka ke dalam implementasi enumerator. : P
Mason Wheeler
2
Tentu saja, tapi itu tidak lagi melintasi array, bukan?
Robert Harvey
4
Karena array adalah use case yang paling umum untuk forloop seperti ini. Bentuk forloop yang saya berikan di sini akan langsung dikenali oleh pengembang mana pun dengan sedikit pengalaman. Jika Anda menginginkan jawaban yang lebih spesifik berdasarkan skenario yang lebih spesifik, Anda harus memasukkannya dalam pertanyaan Anda.
Robert Harvey
3
"Gunakan apa yang terbaik mengekspresikan niatmu" <- Ini!
Jasper N. Brouwer
3
@ JasperN.Brouwer Ini seperti hukum pemrograman nol. Semua aturan gaya dan konvensi pengkodean runtuh dalam rasa malu terdalam ketika mandat mereka bertentangan dengan kejelasan kode.
Iwillnotexist Idonotexist
16

Itu tidak masalah.

Tetapi demi argumen, mari kita menganalisis dua opsi: a > b vs a >= b.

Tunggu sebentar! Itu tidak setara!
OK, lalu a >= b -1vsa > b atau a > bvs a >= b +1.

Hm, a >bdan a >= bkeduanya terlihat lebih baik daripada a >= b - 1dan a >= b +1. Apa sih semua ini 1? Jadi saya berpendapat bahwa manfaat dari memiliki> bukannya >=atau sebaliknya dihilangkan dengan harus menambah atau mengurangi secara acak 1.

Tapi bagaimana kalau itu angka? Apakah lebih baik mengatakan a > 7atau a >= 6? Tunggu sebentar. Apakah kita benar-benar berdebat apakah lebih baik menggunakan >vs >=dan mengabaikan variabel kode keras? Jadi itu benar-benar menjadi pertanyaan apakah a > DAYS_OF_WEEKlebih baik dari a >= DAYS_OF_WEEK_MINUS_ONE... atau itu a > NUMBER_OF_LEGS_IN_INSECT_PLUS_ONEvs a >= NUMBER_OF_LEGS_IN_INSECT? Dan kita kembali menambahkan / mengurangi 1, hanya saja kali ini dalam nama variabel. Atau mungkin berdebat apakah yang terbaik adalah menggunakan ambang, batas, maksimum.

Dan sepertinya tidak ada aturan umum: itu tergantung pada apa yang dibandingkan

Tapi sungguh, ada hal-hal yang jauh lebih penting untuk diperbaiki dalam kode seseorang dan pedoman yang jauh lebih objektif dan masuk akal (misalnya batas karakter-X per baris) yang masih memiliki pengecualian.

Thanos Tintinidis
sumber
1
OKE, tidak ada aturan umum. (Ingin tahu mengapa buku teks itu sepertinya menyatakan aturan umum, tetapi saya bisa membiarkannya pergi.) Tidak ada konsensus yang muncul bahwa itu adalah memo yang saya lewatkan.
2
@nocomprende karena standar pengkodean dan pemformatan murni subyektif dan sangat kontroversial. Sebagian besar waktu tidak ada yang penting, tetapi programmer tetap melakukan perang suci atas mereka. (mereka hanya masalah ketika kode ceroboh cenderung menghasilkan bug)
1
Saya telah melihat banyak diskusi gila tentang standar pengkodean tetapi yang ini mungkin hanya mengambil kue. Sangat konyol.
JimmyJames
@JimmyJames apakah ini tentang diskusi >vs >=atau diskusi tentang apakah diskusi >vs >=bermakna? walaupun mungkin sebaiknya menghindari mendiskusikan ini: p
Thanos Tintinidis
2
“Program dimaksudkan untuk dibaca oleh manusia dan hanya untuk komputer yang dijalankan” - Donald Knuth. Gunakan gaya yang membuatnya paling mudah untuk membaca, memahami, dan memelihara.
Pengguna sederhana
7

Secara komputasi tidak ada perbedaan biaya saat menggunakan <atau >dibandingkan dengan<= atau >=. Ini dihitung sama cepatnya.

Namun sebagian besar untuk loop akan dihitung dari 0 (karena banyak bahasa menggunakan 0 pengindeksan untuk array mereka). Jadi kanonik untuk loop dalam bahasa tersebut adalah

for(int i = 0; i < length; i++){
   array[i] = //...
   //...
}

melakukan ini dengan <=mengharuskan Anda untuk menambahkan -1 di suatu tempat untuk menghindari kesalahan satu per satu

for(int i = 1; i <= length; i++){
   array[i-1] = //...
   //...
}

atau

for(int i = 0; i <= length-1; i++){
   array[i] = //...
   //...
}

Tentu saja jika bahasa menggunakan pengindeksan berbasis 1 maka Anda akan menggunakan <= sebagai syarat pembatas.

Kuncinya adalah bahwa nilai-nilai yang dinyatakan dalam kondisi adalah yang dari deskripsi masalah. Lebih bersih untuk dibaca

if(x >= 10 && x < 20){

} else if(x >= 20 && x < 30){

}

untuk interval setengah terbuka daripada

if(x >= 10 && x <= 19){

} else if(x >= 20 && x <= 29){

}

dan harus melakukan perhitungan matematika untuk mengetahui bahwa tidak ada nilai yang memungkinkan antara 19 dan 20

orang aneh
sumber
Saya melihat ide "panjang", tetapi bagaimana jika Anda hanya menggunakan nilai yang datang dari suatu tempat dan dimaksudkan untuk beralih dari nilai awal ke nilai akhir. Contoh kelas adalah persentase Markup dari 5 hingga 10. Bukankah lebih jelas untuk mengatakan: for (markup = 5; markup <= 10; markup ++) ... ? Mengapa saya harus mengubahnya ke tes: markup <11 ?
6
@nocomprende Anda tidak akan, jika nilai batas dari deskripsi masalah Anda adalah 5 dan 10 maka lebih baik memilikinya secara eksplisit dalam kode daripada nilai yang sedikit berubah.
ratchet freak
@nocomprende Format yang tepat adalah lebih jelas ketika batas atas adalah parameter: for(markup = 5; markup <= MAX_MARKUP; ++markup). Hal lain akan terlalu rumit.
Navin
1

Saya akan mengatakan bahwa intinya bukan apakah Anda harus menggunakan> atau> =. Intinya adalah menggunakan apa pun yang memungkinkan Anda menulis kode ekspresif.

Jika Anda merasa perlu menambah / mengurangi satu, pertimbangkan untuk menggunakan operator lain. Saya menemukan bahwa hal-hal baik terjadi ketika Anda memulai dengan model domain yang baik. Kemudian logika menulis sendiri.

bool IsSpeeding(int kilometersPerHour)
{
    const int speedLimit = 90;
    return kilometersPerHour > speedLimit;
}

Ini jauh lebih ekspresif daripada

bool IsSpeeding(int kilometersPerHour)
{
    const int speedLimit = 90;
    return kilometersPerHour >= (speedLimit + 1);
}

Dalam kasus lain, cara lain lebih disukai:

bool CanAfford(decimal price, decimal balance)
{
    return balance >= price;
}

Lebih baik dari

bool CanAfford(decimal price, decimal balance)
{
    const decimal epsilon = 0e-10m;
    return balance > (price - epsilon);
}

Maafkan "obsesi primitif". Jelas Anda ingin menggunakan jenis Velocity- dan Money-type di sini, tetapi saya menghilangkannya untuk singkatnya. Intinya adalah: Gunakan versi yang lebih ringkas dan yang memungkinkan Anda fokus pada masalah bisnis yang ingin Anda selesaikan.

sara
sumber
2
Contoh batas kecepatan adalah contoh yang buruk. Pergi 90 kpj di zona 90 kpj tidak dianggap melaju kencang. Batas kecepatan sudah termasuk.
eidsonator
Anda benar sekali, kentut otak di pihak saya. merasa bebas untuk mengeditnya untuk menggunakan contoh yang lebih baik, semoga intinya tidak sepenuhnya hilang.
Sara
0

Seperti yang Anda tunjukkan dalam pertanyaan Anda, pengujian untuk kesetaraan pada variabel float tidak dapat diandalkan.

Hal yang sama berlaku untuk <=dan >=.

Namun, tidak ada masalah keandalan seperti itu untuk tipe integer. Menurut pendapat saya, penulis mengungkapkan dirinya pendapat untuk yang lebih mudah dibaca.

Apakah Anda setuju atau tidak dengannya, tentu saja merupakan pendapat Anda .

Dan Pichelman
sumber
Apakah ini pandangan umum, oleh penulis lain dan rambut beruban di lapangan (sebenarnya saya memiliki rambut beruban)? Jika itu hanya "Opini Satu Reporter", saya dapat memberi tahu siswa itu. Sebenarnya sudah. Belum pernah mendengar ide ini sebelumnya.
Jenis kelamin penulis tidak relevan, tetapi saya tetap memperbarui jawaban saya. Saya lebih suka memilih <atau <=berdasarkan pada apa yang paling alami untuk masalah khusus yang saya selesaikan. Seperti yang telah ditunjukkan orang lain, dalam loop UNTUK <lebih masuk akal dalam bahasa tipe-C. Ada kasus penggunaan lain yang mendukung <=. Gunakan semua alat yang Anda inginkan, kapan dan di mana sesuai.
Dan Pichelman
Tidak setuju. Ada dapat menjadi masalah reliabilitas dengan tipe integer: di C, mempertimbangkan: for (unsigned int i = n; i >= 0; i--)atau for (unsigned int i = x; i <= y; i++)jika yterjadi menjadi UINT_MAX. Ups, loop itu selamanya.
jamesdlin
-1

Setiap hubungan <, <=, >=, >dan juga == dan !=memiliki mereka gunakan-kasus untuk membandingkan dua nilai floating-point. Masing-masing memiliki makna spesifik dan yang sesuai harus dipilih.

Saya akan memberikan contoh untuk kasus di mana Anda ingin operator ini untuk masing-masingnya. (Namun, waspadai NaNs.)

  • Anda memiliki fungsi murni yang mahal fyang menggunakan nilai titik-mengambang sebagai input. Dalam rangka untuk mempercepat perhitungan Anda, Anda memutuskan untuk menambahkan cache dari nilai yang paling baru-baru dihitung, yaitu, pemetaan tabel xuntuk f(x). Anda benar-benar ingin menggunakan ==untuk membandingkan argumen.
  • Anda ingin tahu apakah Anda dapat membagi secara bermakna dengan beberapa nomor x? Anda mungkin ingin menggunakannya x != 0.0.
  • Anda ingin tahu apakah suatu nilai xberada dalam interval satuan? (x >= 0.0) && (x < 1.0)adalah kondisi yang benar.
  • Anda telah menghitung determinan dsuatu matriks dan ingin memberi tahu apakah itu pasti positif? Tidak ada alasan untuk menggunakan hal lain selain d > 0.0.
  • Anda ingin tahu apakah Σ n = 1,…, ∞ n −α berbeda? Saya akan menguji alpha <= 1.0.

Matematika floating-point (secara umum) tidak tepat. Tetapi itu tidak berarti bahwa Anda harus takut, memperlakukannya sebagai sihir, dan tentu saja tidak selalu memperlakukan dua jumlah titik-mengambang sama jika mereka berada di dalam 1.0E-10. Melakukan hal ini akan benar-benar mematahkan matematika dan menyebabkan semua hal-hal aneh terjadi.

  • Misalnya, jika Anda menggunakan perbandingan fuzzy dengan cache yang disebutkan di atas, Anda akan memperkenalkan kesalahan lucu. Tetapi lebih buruk lagi, fungsi tidak akan lagi murni dan kesalahan tergantung pada hasil yang dihitung sebelumnya!
  • Ya, bahkan jika x != 0.0dan ymerupakan nilai floating-point yang terbatas, y / xtidak perlu terbatas. Tetapi mungkin relevan untuk mengetahui apakah y / xtidak terbatas karena melimpah atau karena operasi tidak secara matematis didefinisikan dengan baik untuk memulai.
  • Jika Anda memiliki fungsi yang memiliki prasyarat bahwa parameter input xharus berada dalam interval unit [0, 1), saya akan benar-benar kesal jika ia mengeluarkan kegagalan pernyataan ketika dipanggil dengan x == 0.0atau x == 1.0 - 1.0E-14.
  • Penentu yang Anda hitung mungkin tidak akurat. Tetapi jika Anda akan berpura-pura bahwa matriks tidak pasti positif ketika determinan yang dihitung adalah 1.0E-30, tidak ada yang diperoleh. Yang Anda lakukan hanyalah meningkatkan kemungkinan memberikan jawaban yang salah.
  • Ya, argumen Anda alphamungkin dipengaruhi oleh kesalahan pembulatan dan oleh karena itu alpha <= 1.0mungkin benar meskipun nilai matematika sebenarnya untuk ekspresi alphadihitung dari mungkin benar-benar lebih besar dari 1. Tapi tidak ada yang bisa Anda lakukan tentang hal itu pada saat ini.

Seperti biasa dalam rekayasa perangkat lunak, menangani kesalahan pada tingkat yang sesuai dan menanganinya hanya sekali. Jika Anda menambahkan kesalahan pembulatan pada urutan 1.0E-10(ini tampaknya merupakan nilai ajaib yang digunakan kebanyakan orang, saya tidak tahu mengapa) setiap kali Anda membandingkan jumlah titik-mengambang, Anda akan segera melakukan kesalahan pada urutan 1.0E+10...

5gon12eder
sumber
1
Jawaban yang paling bagus, meskipun diakui, hanya untuk pertanyaan yang berbeda dari yang diajukan.
Dewi Morgan
-2

Jenis kondisional yang digunakan dalam satu lingkaran dapat membatasi jenis optimisasi yang dapat dilakukan oleh kompiler, baik atau buruk. Misalnya, diberikan:

uint16_t n = ...;
for (uint16_t i=1; i<=n; i++)
  ...  [loop doesn't modify i]

kompilator dapat berasumsi bahwa kondisi di atas harus menyebabkan loop untuk keluar setelah loop ke-n kecuali kecuali n 65535 dan loop mungkin keluar dalam beberapa cara selain oleh i melebihi n. Jika ketentuan tersebut berlaku, kompiler harus membuat kode yang akan menyebabkan loop dijalankan hingga sesuatu selain kondisi di atas menyebabkannya keluar.

Jika lingkaran itu malah ditulis sebagai:

uint16_t n = ...;
for (uint16_t ctr=0; ctr<n; ctr++)
{
  uint16_t i = ctr+1;
  ... [loop doesn't modify ctr]
}

maka kompiler dapat dengan aman mengasumsikan bahwa loop tidak akan pernah perlu mengeksekusi lebih dari n kali dan dengan demikian dapat menghasilkan kode yang lebih efisien.

Perhatikan bahwa setiap luapan dengan tipe yang ditandatangani dapat memiliki konsekuensi buruk. Diberikan:

int total=0;
int start,lim,mult; // Initialize values somehow...
for (int i=start; i<=lim; i++)
  total+=i*mult;

Kompiler mungkin menulis ulang itu sebagai:

int total=0;
int start,lim,mult; // Initialize values somehow...
int loop_top = lim*mult;
for (int i=start; i<=loop_top; i+=mult)
  total+=i;

Perulangan seperti itu akan berperilaku identik dengan yang asli jika tidak ada overflow yang terjadi dalam perhitungan, tetapi dapat berjalan selamanya bahkan pada platform perangkat keras di mana integer overflow biasanya memiliki semantik pembungkus yang konsisten.

supercat
sumber
Ya, saya pikir itu sedikit lebih jauh di dalam buku teks. Belum menjadi pertimbangan. Optimalisasi Kompiler berguna untuk dipahami, dan meluap harus dihindari, tetapi itu bukan motivasi untuk pertanyaan saya, yang lebih terkait dengan cara orang memahami kode. Apakah lebih mudah membaca x> 5 atau x> = 6? Yah, itu tergantung ...
Kecuali Anda menulis untuk Arduino, nilai maksimal untuk int akan sedikit lebih tinggi dari 65535.
Corey
1
@Corey: Nilai maksimal untuk uint16_t akan menjadi 65.535 pada platform apa pun di mana jenisnya ada; Saya menggunakan uint16_t dalam contoh pertama karena saya berharap lebih banyak orang mengetahui nilai maksimum yang tepat dari uint16_t daripada nilai uint32_t. Poin yang sama berlaku dalam kedua kasus tersebut. Contoh kedua adalah agnostik sehubungan dengan jenis "int", dan merupakan titik perilaku kritis yang banyak orang gagal untuk mengenali tentang kompiler modern.
supercat