'<' versus '! =' bir 'for' döngüsünde koşul olarak?


31

Diyelim ki aşağıdaki fordöngü *:

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

yaygın olarak şu şekilde de yazılabilir:

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

Nihai sonuçlar aynıdır, yani birini üst üste kullanmanın gerçek argümanları var mı? Şahsen ben eski kullanırım inedense haywire gider ve 10 değerini atlar.

* Sihirli sayıları kullanmak için özür dilerim, ama bu sadece bir örnek.


18
Bunlardan herhangi birine üstün çözüm ok operatörünü kullanmaktır:int i = 10; while(i --> 0) { /* stuff */ }
corsiKa

@glowcoder ok operatörü benim favorim
Carson Myers

3
@glowcoder, güzel ama arkadan geçer. Bunu her zaman istemeyebilirsin.

2
@ SüperElectric, gibi ayrıştırırwhile ((i--) > 0)
Kristopher Johnson

17
Bir sensör girişi için bir süre döngü testi nedeniyle neden olduğu bir endüstriyel kaza hakkında (muhtemelen apocryphal) bir hikaye var! = MAX_TEMP. Maalesef bir gün sensör girişi, MAX_TEMP'den her biri geçmeden MAX_TEMP'den daha az ve MAX_TEMP'den daha yüksek bir seviyeye çıktı. İşlem algılanmadan aşırı ısındı ve bir yangın çıktı. Bazen! = Ve <arasında bir fark vardır.
Charles E. Grant

Yanıtlar:


59

Birini veya diğerini seçmenin nedeni, niyet nedeniyledir ve bunun sonucunda okunabilirliği arttırır .

Amaç: Döngü i10'dan küçük olduğu sürece, i10'a eşit olmadığı sürece çalıştırılmamalıdır . Bu durumda, ikincisi aynı olsa da, demek istediğin gibi değil, bu yüzden yapmamalı. böyle yazılmak.

Okunabilirlik: ne demek istediğinizi yazmanın bir sonucu da, anlaşılmasının daha kolay olduğu. Örneğin i != 10, kodu okuyorsanız, kodu okuyan bir kişi döngünün içinde bir yolun i10'dan büyük olabileceğini ve döngünün devam edip etmeyeceğini merak edebilir (btw: yineleyiciyle kafasından başka bir yere karıştırmak kötü bir stildir). for) -Bildirim, ama bu demek insanlar bunu yapmayın değildir ve sonuç olarak bakım yapanlar bunu bekliyoruz.


3
Hayır, döngü i“10” dan küçük olduğu sürece çalışmamalı, (bu durumda) üst sınıra ulaşılmadığı sürece çalışmalıdır. C ++ interval / yineleyici aralığı mantığını kullanırken, bunu ifade !=etmekten çok daha mantıklı olur <.
Konrad Rudolph

14
@Konrad Buna kesinlikle katılmıyorum. Asıl nokta dogmatik olmak değil, koşulun amacını en iyi ifade eden yapıyı seçmek. Ve böylece, 0'dan başlayıp yukarı doğru hareket ederek bir şeyden geçmeyi seçerseniz, <aslında bunu tam olarak ifade eder.
Deckard

6
@Konrad, noktayı kaçırıyorsunuz. Bu döngüdeki artış operatörü, döngü koşulunun bir kimlik karşılaştırması değil, bir üst sınır olduğunu açıkça ortaya koyar. Azalsaydın, daha düşük bir sınır olurdu. Sipariş verilmeyen bir koleksiyonu yineliyorsanız, kimlik doğru koşul olabilir.
Alex Feinman

3
@Alexs artış benim amacım değildi. Bir aralığın kesin olarak bir emri bile yoktur . Sadece tekrar eder ... şey , sonuna kadar bir şey . Örneğin, bir karma kümedeki öğeler. C ++ 'da, yineleyiciler ve bir fordöngü kullanılarak yapılır . <Burada kullanmak aptalca olurdu. Ayrıca, Deckard bunu kabul ediyor gibi görünüyor. Benim cevabına cevaben yorumuna katılıyorum.
Konrad Rudolph

1
Not: Kovalayıcı işaretçilerle birlikte döner bir tampon kullanıyorsanız,!=
SF

48

<Model artış tam olarak 1 olması için bir sorun olsa bile, genel olarak kullanılabilir.

Bu, gerçekte nasıl yapıldığına bakılmaksızın, tek bir ortak yolun döngüler yapmasını sağlar.


6
Ayrıca igerektiğinde döngünün içinde tamamen güvenli bir şekilde değiştirilebilmesini sağlar .
Matthew Scharley

1
ya da 'i' tamamen güvenli olmayan şekilde değiştirilirse ... Başka bir takımın tuhaf bir sunucu sorunu vardı. % 100 CPU kullanımını rapor etmeyi sürdürdü ve sunucuda veya izleme sisteminde bir sorun olmalı, değil mi? Hayır, konuştuğumuz problemin aynısı olan bir 'uzman kıdemli programcı' tarafından yazılmış bir döngü koşulu buldum.
jqa

12
Bunun haricinde, <bir dizi üzerindeki yineleyiciler gibi tüm döngüler için C ++ kullanılamaz ve bu nedenle !=C ++ ile döngü yapmak için tek bir ortak yol olabilir.
David Thornley

@SnOrfus: Bu yorumu tam olarak ayrıştırmıyorum. Genel olarak, bir yineleyiciyi çok fazla arttırmak için istisnalar dışında güvenilir bir şekilde istisnalar elde edemezsiniz.
David Thornley

<Deseni izliyorum, çünkü desenle iki yerine bir tuşa basmayı >ve dört ile de bir tuşa basmayı gerektirir !=. Gerçekten de OKB benzeri bir verimlilik meselesi.
Spoike

21

C ++ 'da Scott Myers'ın Daha Etkili C ++' daki (madde 6) tavsiyesi, yinelemeci ve tamsayı indeksleri için aynı sözdizimine sahip olmanız anlamına gelmediğinden, gerekmediğiniz bir neden olmadıkça daima intikinciyi kullanmaktır. sözdiziminde herhangi bir değişiklik olmadan.

Diğer dillerde bu geçerli değildir, bu yüzden <Thorbjørn Ravn Andersen’nın meselesi nedeniyle muhtemelen tercih edildiğini düşünüyorum.

Bu arada, geçen gün derken başka bir geliştirici ile bu tartışmaya ve o tercih sebebi söz konusu <aşırı !=nedeni ikudretinin yanlışlıkla birden fazla aralıklara ve bu kırılma koşul yerine olmamak neden olabilir; Bu IMO saçmalık bir yük.


17
"saçmalık yük" ... döngünün gövdesinde kazara fazladan bir i ++ olduğu güne kadar.

2
+1, özellikle “saçmalık” için, çünkü öyle. Döngü gövdesi yanlışlıkla sayacı arttırırsa, çok daha büyük sorunlarınız olur.
Konrad Rudolph

12
@B Tyler, biz sadece insanız ve daha önce büyük hatalar oldu. O zamandan <daha fazla savunma yazılımı olarak düşünün !=...

2
@ Thorbjørn Ravn Andersen - Sana katılmıyorum demiyorum demiyorum; <daha savunucudur ve yazarsanız yazmazsam meslektaşlarım bundan nefret eder !=. Ancak, !=C ++ için bir durum var ve sunduğum şey bu.

9
Kazara bir fazlalıkla sonuçlanabilecek bir senaryo i++, bir süre döngüsünü for döngüsüne dönüştürmektir. İterasyon sayısının yarısına sahip olmanın (yakın) bir sonsuz döngüden daha iyi olduğundan emin değilsiniz; ikincisi muhtemelen böceği daha belirgin hale getirecektir.
ak2

12

(İ <10) kullanmak bence daha güvenli bir uygulama. Potansiyel bırakma vakalarının azami sayısını yakalar - 10'dan büyük veya 10'a eşit olan her şey. Bunu diğer durumla karşılaştırın (i! = 10); Sadece bir olası bırakma olayını yakalar - tam olarak 10 yaşındayken.

Bunun bir yan ürünü okunabilirliği arttırmasıdır. Ek olarak, artış başka bir şey olursa 1, bırakma vakasını yazarken hata yapmamız durumunda sorun olasılığını en aza indirmeye yardımcı olabilir.

Düşünmek:

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

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

Her iki durumda da hatalı / yanlış olmasına rağmen, ikincisinin bırakmayacağından DAHA yanlış olması muhtemeldir. İlk vaka bırakılacak ve 14 muhtemelen yanlış sayı olsa da (15 muhtemelen daha iyi olacak) doğru yerde bırakma olasılığı daha yüksek.


İlk dava doğru olabilir! 14'ten küçük tüm doğal sayıları yinelemek istiyorsanız, ifade etmenin daha iyi bir yolu yoktur - "uygun" üst sınırın (13) hesaplanması salakça olacaktır.
maaartinus

9

İşte kimsenin henüz gelmemiş gibi göründüğü başka bir cevap. forBir dizi üzerinde yineleme yapmanız gerektiğinde döngüler kullanılmalıdır . Kullanımı !=, döngünün sonlandırma koşulunu belirtmek için en özlü yöntemdir. Ancak, daha az kısıtlayıcı bir operatör kullanmak çok yaygın bir savunma programlama deyimidir. Tamsayılar için önemli değil - bu daha spesifik bir örnek olmadan sadece kişisel bir seçimdir. !=Başkalarının belirttiği nedenlerden kullanmak istediğiniz yineleyicilerle koleksiyonların üzerinden geçerken . floatVeya dizilerini düşünüyorsanız double, o zaman ne !=pahasına olursa olsun kaçınmak istersiniz .

İşaret etmek istediğim, forbir dizi üzerinde yineleme yapmanız gerektiğinde kullanılan şey. Oluşturulan dizinin bir başlangıç ​​noktası, bir aralığı ve bir sonlandırma koşulu vardır. Bunlar kesinlikle forbeyan içerisinde belirtilmiştir . Kendinizi ya da (1) forya da (2) truekoruyucu koşulu gibi bir şey belirten adım bölümünü içermeyen buluyorsanız , bir fordöngü kullanmamalısınız !

whileDöngü, belirli bir durum karşılamakta olup işleme devam etmek için kullanılır. Bir diziyi işlemiyorsanız, muhtemelen whilebunun yerine bir döngü istiyorsunuz . Bekçi koşulu argümanları burada benzer, ancak a whileve forloop arasındaki karar çok bilinçli olmalıdır. whileIMO C ++ çevrelerinde altında takdir döngüdür.

Bir öğe koleksiyonunu (çok yaygın bir fordöngü kullanımı) işliyorsanız, gerçekten daha özel bir yöntem kullanmalısınız. Ne yazık ki, std::for_eachC ++ 'da bir çok sebepten dolayı oldukça acı verici. Birçok durumda, bir forilmek gövdesini serbest duran bir fonksiyonda (biraz ağrılı iken) ayırmak, daha temiz bir çözelti ile sonuçlanır. Koleksiyonları ile çalışırken, düşünün std::for_each, std::transformya std::accumulate. Birçok algoritmanın uygulanması, bu şekilde ifade edildiğinde kısa ve netleşir. Döngünün gövdesini ayrı bir işleve / yönteme ayırmanın sizi algoritmaya, giriş gereksinimlerine ve sonuçlarına odaklanmaya zorladığından bahsetmiyorum.

Java, Python, Ruby, hatta kullanıyorsanız C ++ 0x , o zaman uygun bir koleksiyon kullanarak olmalıdır foreach döngü. C ++ derleyicileri bu özelliği uygularken for, bu tür tartışmalarda olduğu gibi çok sayıda döngü kaybolacaktır.


2
"Ancak, daha az kısıtlayıcı bir operatör kullanmak çok yaygın bir savunma programlama deyimidir." Bu, daha fazla veya daha az özetler.
James P.,

Karşılaştırıldığında ile niyet farklılıkları konuşuyorduk için 1 while, forve foreach(veya dil equivilant)
Kevin Peno

Tam anlam belirli bir koşul kadar halka işlemini devam etmek için kullanılır değil buluştuwhile
Alnitak

@Alnitak - D'oh ... Bunu düzelteceğim :)
D.Shawley

"Daha az kısıtlayıcı" olan iki olası anlamla kafam karıştı: bu, operatörün geçtiği değerlerde ( <) ya da operatörün başarısız olduğu değerlerde ( !=) değerinde esnek olduğunu gösteriyor .
Mateen Ulhaq

4

"Küçükten küçük" ifadesi (genellikle) anlamsal olarak doğru ise, gerçekten i10'dan küçük olana kadar saymayı kastediyorsunuz , bu yüzden "küçükten az" niyetinizi açıkça ifade ediyor. "Eşit olmayan" kullanımı hemen hemen çağrı durumlarında açıkça çalışır, ancak biraz farklı bir anlam ifade eder. Bazı durumlarda ihtiyacınız olan şey olabilir ama benim tecrübeme göre bu asla böyle olmadı. Gerçekten i10'dan daha az ya da daha az olabileceği bir durumunuz olsaydı ancak 10'a eşit olana kadar döngü devam ettirmek istiyorsanız, o zaman bu kodun gerçekten çok net bir şekilde yorum yapması gerekirdi ve muhtemelen başka bir yapıyla daha iyi yazılmış olabilirdi. whilebelki bir döngü olarak .


2

Bir iyilik istiyorum nedenlerinden biri less thanaşkın not equalsbir bekçi olarak hareket etmektir. Bazı sınırlı durumlarda (kötü programlama veya sterilizasyon) yine not equalsde geçerli olacak şekilde atlanabilir less than.

İşte bir temizlik kontrolünün eksikliğinin tuhaf sonuçlara yol açtığı bir örnek: http://www.michaeleisen.org/blog/?p=358


0

İşte <yerine tercih etmenin bir nedeni !=. Genel değişken kapsamı olan bir dil kullanıyorsanız, diğer kod değiştirilirse ne olur i? Daha <ziyade !=, en kötüsü, yinelemenin daha çabuk bitmesidir: belki de iyanlışlıkla bazı kod artışları olabilir ve for döngüsünde birkaç yinelemeyi atlarsınız. Fakat 0'dan 10'a kadar döngü oluşturuyorsanız ne olur ve döngü 9'a, ibazı garip nedenlerden dolayı bazı kötü yazılmış iplik artışları . Ardından, döngünüz bu yinelemeyi ve artışları tamamlar, iböylece değer şimdi 11 olur. Döngü çıkış koşulunu atladığında ( iasla 10'a eşit olmaz) şimdi sonsuz bir şekilde dönecektir.

Bu duruma neden olacak özellikle garip bir iş parçacığı ve genel değişkenler türü mantığı olması gerekmez. Sadece geri izlemesi gereken bir döngü yazıyor olabilirsiniz. iDöngünün içinde mutasyona uğruyorsanız ve mantığınızı berbat ediyorsanız, onu !=bir sonsuzluk sınırında bırakma olasılığınızın düşük olması yerine, bir üst sınırına sahip olmasını sağlayın .


3
Tabii ki, bu her döngü için mükemmel bir argüman ve genel olarak daha işlevsel bir programlama tarzı gibi görünüyor. Ancak, değişken değişkenler çok daha eğlenceli olduğunda neden bunu yapmak istiyorsunuz ?
Tom Morris,

3
Bunun çok iyi bir sebep olduğunu sanmıyorum. Her iki durumda da bulunması ve düzeltilmesi gereken bir böcek var, ancak sonsuz bir döngü bir böceği daha açık bir hale getirme eğilimindedir.
ak2

0

Gömülü dünyada, özellikle gürültülü ortamlarda, mutlaka olması gerektiği gibi davranan RAM'e güvenemezsiniz. '==' veya '! =' Kullanarak karşılaştırdığınız koşullu (yani, eğer) ise, değişkenlerinizin her zaman döngüyü sona erdiren kritik değeri atlaması riskini atlarsınız - bu felaket sonuçlara neden olabilir - Mars Lander seviye sonuçları. Bu durumda '<' veya '>' kullanımı, 'bilinmeyen bilinmeyenleri' yakalamak için ekstra güvenlik sağlar.

Orijinal örnekte, eğer iaçıklanamaz şekilde 10'dan daha büyük bir değere mandalina olsaydı , '<' karşılaştırması hatayı hemen yakalar ve döngüden çıkardı, ancak '! =', i0 ve sonlarına kadar sarılıncaya kadar saymaya devam edecekti. 10'a.


2
İlgili başka bir varyasyon for (i=4; i<length; i++) csum+=dat[i];, meşru paketlerin her zaman lengthen az dörtte sahip olacağı bir kodla mevcuttur , ancak bir tanesi bozuk paketler alabilir. Farklı paket tipleri farklı uzunluk gereksinimlerine sahipse, uzunluk, her paket tipi için farklı olan işlemin bir parçası olarak uzunluğun doğrulanması, ancak genel ortak mantığın bir parçası olarak sağlama toplamının doğrulanması istenebilir. Bir alt paket alındığında, sağlama toplamı hesaplamasının "iyi" veya "kötü" olup olmadığının önemi yoktur, ancak çökmemesi gerekir.
supercat
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.