For döngüsünde break kullanmak kötü bir uygulama mı? [kapalı]


123

Bir döngü içinde breakifade kullanmak kötü bir uygulama mı?for

Diyelim ki bir dizide bir değer arıyorum. Bir for döngüsü içinde ve değer bulunduğunda, break;for döngüsünden çıkmak için karşılaştırın.

Bu kötü bir uygulama mı? Kullanılan alternatifi gördüm: bir değişken tanımlayın vFoundve değer bulunduğunda bunu doğru olarak ayarlayın vFoundve forifade koşulunu kontrol edin . Ama sadece bu amaç için yeni bir değişken yaratmak gerekli mi?

Normal bir C veya C ++ for döngüsü bağlamında soruyorum.

Not: MISRA kodlama yönergeleri , ara kullanılmaması gerektiğini belirtir .


28
breakgoto
İle

2
Eee, onları go ile aynı lige koymadım ... MISRA kuralları doğru hatırlıyorsam mola veriyor.
kiki

1
Bir döngü içinde break kullanmamayı düşünmemin tek nedeni, yaptığınız şeyin sonucunu değiştirebilecek daha fazla öğeyi işlemeye devam etmeniz gerektiğidir ...
Younes


Yanıtlar:


122

Burada pek çok cevap var, ancak bundan bahsedildiğini henüz görmedim:

Düzenli, kolay okunabilir döngüler yazarsanız, bir for döngüsü kullanmakla breakveya continuebir for döngüsü içinde ilişkili "tehlikelerin" çoğu reddedilir. Döngünüzün gövdesi birkaç ekran uzunluğunu kapsıyorsa ve birden çok iç içe geçmiş alt bloğa sahipse, evet, aradan sonra bazı kodların çalıştırılmayacağını kolayca unutabilirsiniz. Bununla birlikte, döngü kısa ve öz ise, break ifadesinin amacı açık olmalıdır.

Bir döngü çok büyürse, bunun yerine döngü içinde bir veya daha fazla iyi adlandırılmış işlev çağrısı kullanın. Bunu yapmaktan kaçınmanın tek gerçek nedeni, darboğazları işlemektir.


11
Sessiz doğru. Elbette, döngü o kadar büyük ve karmaşıksa, içinde ne olduğunu görmek zorsa, mola verip vermemeniz bir sorundur.
Jay

1
Diğer bir sorun da, ara ve devam et, yeniden düzenleme ile sorunlara neden olmasıdır. Bu, bunun kötü bir uygulama olduğuna dair bir işaret olabilir. Kodun amacı, if ifadeleri kullanıldığında da daha nettir.
Ed Greaves

Bu "iyi adlandırılmış işlev çağrıları", başka bir yerde kullanılmayacak harici olarak tanımlanmış özel işlevler yerine yerel olarak tanımlanmış lambda işlevleri olabilir.
DavidRR

135

Hayır, mola doğru çözümdür.

Bir boole değişkeninin eklenmesi, kodun okunmasını zorlaştırır ve olası bir hata kaynağı ekler.


2
kabul. Özellikle döngüden birden fazla koşulda çıkmak istiyorsanız. Bir döngüden ayrılan boole, o zaman gerçekten kafa karıştırıcı olabilir.
Rontologist

2
Bu yüzden gotoSeviye 2+ iç içe döngülerden çıkmak için kullanıyorum - çünkü yokbreak(level)
Петър Петров

3
Döngü kodunu bir işleve koyup geri dönmeyi tercih ederim. Bu, gitmeyi önler ve kodunuzu daha küçük parçalara böler.
Dave Branton

53

İçlerinde 'kırılma' ifadeleri bulunan her tür profesyonel kodu bulabilirsiniz. Bunu gerektiğinde kullanmak tamamen mantıklı. Sizin durumunuzda bu seçenek, döngüden çıkmak amacıyla ayrı bir değişken oluşturmaktan daha iyidir.


47

Bir döngüde olduğu breakkadar kullanmak da gayet iyi.continuefor

Kodu basitleştirir ve okunabilirliğini artırır.


Evet .. Bir süre için bu fikri beğenmedim ve etrafına kod yazdım, ancak "geçici çözümün" genellikle bir bakım kabusu olduğunu fark etmem çok uzun sürmedi. Kabus değil, hataya daha yatkın.
MetalMikester

24

Kötü uygulamadan uzak olarak, Python (ve diğer diller?) forDöngü yapısını genişletti, böylelikle bir kısmı sadece döngü yapmazsa çalıştırılacak break .

for n in range(5):
    for m in range(3):
        if m >= n:
            print('stop!')
            break
        print(m, end=' ')
    else:
        print('finished.')

Çıktı:

stop!
0 stop!
0 1 stop!
0 1 2 finished.
0 1 2 finished.

Eşdeğer kod olmadan breakve bu kullanışlı else:

for n in range(5):
    aborted = False
    for m in range(3):
        if not aborted:
            if m >= n:
                print('stop!')
                aborted = True
            else:            
                print(m, end=' ')
    if not aborted:
        print('finished.')

2
Oooh, bunu beğendim ve bazen C de diledim. Nice sözdizimi, belirsizlik olmadan gelecekteki C benzeri bir dile uyarlanabilir.
supercat

"C benzeri dil": P
nirvanaswap

15

Break ifadesini kullanmanın doğası gereği yanlış bir şey yoktur, ancak iç içe geçmiş döngüler kafa karıştırıcı olabilir. Okunabilirliği artırmak için birçok dil ( en azından Java destekler) okunabilirliği büyük ölçüde artıracak etiketlerin kırılmasını destekler.

int[] iArray = new int[]{0,1,2,3,4,5,6,7,8,9};
int[] jArray = new int[]{0,1,2,3,4,5,6,7,8,9};

// label for i loop
iLoop: for (int i = 0; i < iArray.length; i++) {

    // label for j loop
    jLoop: for (int j = 0; j < jArray.length; j++) {

        if(iArray[i] < jArray[j]){
            // break i and j loops
            break iLoop;
        } else if (iArray[i] > jArray[j]){  
            // breaks only j loop
            break jLoop;
        } else {
            // unclear which loop is ending
            // (breaks only the j loop)
            break;
        }
    }
}

Break (ve return) ifadelerinin çoğu zaman döngüsel karmaşıklığı artırdığını ve kodun her durumda doğru şeyi yaptığını kanıtlamayı zorlaştırdığını söyleyeceğim.

Belirli bir öğe için bir dizi üzerinde yinelerken bir ara kullanmayı düşünüyorsanız, verilerinizi tutmak için kullanılan veri yapısını yeniden gözden geçirmek isteyebilirsiniz. Set veya Harita gibi bir şey kullanmak daha iyi sonuçlar sağlayabilir.


4
Siklomatik karmaşıklık için +1.
sp00m

.NET programcıları , karmaşık döngülere potansiyel bir alternatif olarak LINQ kullanımını keşfetmek isteyebilir for.
DavidRR

14

ara kullanımı için tamamen kabul edilebilir bir ifadesidir (so devam btw). Her şey kod okunabilirliğiyle ilgili - aşırı karmaşık döngüleriniz olmadığı sürece sorun değil.

Goto ile aynı ligde değiller . :)


Bunun bir mola ifadesi olduğunu savundu gördük olduğunu etkili bir şekilde git .
glenatron

3
Goto ile ilgili sorun, onu herhangi bir yere işaret edebilmenizdir. Hem kırılır hem de devam eder, kodun modülerliğini korur. Bu argüman, her işlev için tek bir çıkış noktasına sahip olmakla aynı düzeydedir.
Zachary Yates

1
Bunun bir işlev için birden çıkış noktalarını sahip savundu duydum olduğunu etkili bir şekilde git . ; D
glenatron

2
@glenatron Goto ile ilgili sorun goto ifadesi değil, sorun olan goto'nun atladığı etiketin girişidir. Bir goto cümlesi okuduğunuzda, kontrol akışı hakkında hiçbir belirsizlik yoktur. Bir etiketi okuduğunuzda, kontrol akışı hakkında çok fazla belirsizlik vardır (kontrolü bu etikete aktaran tüm gotolar nerede?). Break ifadesi bir etiket getirmez, bu nedenle herhangi bir yeni komplikasyon getirmez.
Stephen C. Steel

1
"Kırılma", yalnızca bloklar bırakabilen özel bir gitmedir. "Goto" ile ilgili tarihsel sorunlar, (1) uygun kontrol yapılarıyla mümkün olmayan bir şekilde üst üste binen döngü blokları oluşturmak için kullanılmış olabilirdi ve sıklıkla kullanılıyordu; (2) Bir "eğer-ise" yapısının genellikle "yanlış" olan yaygın bir yolu, gerçek durumun kodda ilgisiz bir yere ayrılması ve sonra geri dallanmasıdır. Bu, nadir durumlarda iki şubeye mal olur ve yaygın durumda sıfıra mal olur, ancak kodu iğrenç görünmesine neden olur.
supercat

14

Genel kural: Bir kurala uymak, daha garip ve okunması zor bir şey yapmanızı, sonra kuralı çiğnemenizi gerektiriyorsa, o zaman kuralı çiğneyin.

Bir şey bulana kadar döngü halinde, çıktığınızda bulunan ve bulunmayanları ayırt etme problemiyle karşılaşırsınız. Yani:

for (int x=0;x<fooCount;++x)
{
  Foo foo=getFooSomehow(x);
  if (foo.bar==42)
    break;
}
// So when we get here, did we find one, or did we fall out the bottom?

Tamam, bir bayrak ayarlayabilir veya "bulunan" bir değeri sıfır olarak başlatabilirsiniz. Fakat

Bu yüzden genel olarak aramalarımı işlevlere itmeyi tercih ediyorum:

Foo findFoo(int wantBar)
{
  for (int x=0;x<fooCount;++x)
  {
    Foo foo=getFooSomehow(x);
    if (foo.bar==wantBar)
      return foo;
  }
  // Not found
  return null;
}

Bu aynı zamanda kodun dağınıklığına da yardımcı olur. Ana satırda, "bul" tek bir cümle haline gelir ve koşullar karmaşık olduğunda, yalnızca bir kez yazılır.


1
Tam olarak söylemek istediğim şey. Genellikle kendimi kullanırken breakbulduğumda, kodu bir işleve dönüştürmenin bir yolunu bulmaya çalışırım, böylece returnonun yerine kullanabilirim .
Rene Saarsoo

Size% 100 katılıyorum, break kullanmanın doğası gereği yanlış bir şey yok. Bununla birlikte, genellikle içinde bulunduğu kodun, sonun return ile değiştirileceği bir işleve çıkarılması gerekebileceğini belirtir. Kesin bir hata olmaktan ziyade bir 'kod kokusu' olarak düşünülmelidir.
vascowhite

Cevabınız, bazılarını çoklu dönüş ifadeleri kullanmanın tavsiye edilebilirliğini keşfetmeye sevk edebilir .
DavidRR

13

Dile bağlıdır. Burada bir boole değişkenini muhtemelen kontrol edebilirsiniz:

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

bir dizi üzerinde yineleme yaparken bunu yapmak mümkün değildir:

for element in bigList: ...

Her neyse, breakher iki kodu da daha okunaklı hale getirir.


7

Örneğinizde, for döngüsü için yineleme sayısını bilmiyorsunuz . Bunun yerine neden yineleme sayısının başlangıçta belirsiz olmasına izin veren while döngüsü kullanılmasın?

Döngü bir while döngüsü olarak daha iyi ifade edilebileceğinden, genel olarak break ifadesini kullanmak gerekli değildir .


1
Bilinen bir maksimum yineleme sayısı varsa, "for" döngüsü genellikle iyidir. Bununla birlikte, bir süre (1) döngüsü, kişinin bir öğeyi aradığı ve yoksa onu yaratmayı planladığı bir senaryoda bazen daha iyidir. Bu durumda, kod öğeyi bulursa doğrudan bir kesme yapar ve dizinin sonuna ulaşırsa yeni bir öğe oluşturur ve ardından bir kesme yapar.
supercat

2
Verilen örnekte - bir anahtar için bir dizide arama yaparken, for döngüsü uygun deyimsel yapıdır. Bir dizide gezinmek için while döngüsü kullanmazsınız. Bir for döngüsü kullanıyorsunuz. (veya varsa her döngü için). Bunu, "for (int i = 0; i <ra.length; i ++) {}" yapısını hemen "Dizide gezin" olarak tanıdıklarından, diğer programcılara nezaketen yaparsınız. Bu yapıya bir ara eklemek basitçe "Yapabiliyorsanız erken çıkın" ifadesi.
Chris Cudmore

7

Kullanmayı öneren diğerlerine katılıyorum break. Sonuç olarak ortaya çıkan bariz soru, neden birinin aksini tavsiye etmesidir? Peki ... break kullandığınızda, bloktaki kodun geri kalanını ve kalan yinelemeleri atlarsınız. Bazen bu, hatalara neden olur, örneğin:

  • bloğun tepesinde elde edilen bir kaynak altta serbest bırakılabilir (bu, fordöngülerin içindeki bloklar için bile geçerlidir ), ancak bu serbest bırakma adımı, bir breakifadeden ("modern" de) "erken" bir çıkışa neden olduğunda yanlışlıkla atlanabilir. C ++, "RAII", bunu güvenilir ve istisna korumalı bir şekilde ele almak için kullanılır: temel olarak, nesne yıkıcılar, kapsamdan nasıl çıkılırsa çıkılsın, kaynakları güvenilir bir şekilde serbest bırakır)

  • başka biri for, başka yerinden ayrılmış çıkış koşulları olduğunu fark etmeden ifadedeki koşullu testi değiştirebilir

  • ndim'in cevabı, bazı kişilerin breaknispeten tutarlı bir döngü çalışma süresi sağlamaktan kaçınabileceğini gözlemliyor , ancak breakbunun tutmadığı bir boole erken çıkış kontrol değişkeninin kullanımıyla karşılaştırıyorsunuz

Arada sırada bu tür hataları gözlemleyen insanlar, bu "kesinti yok" kuralı ile bunların önlenebileceğini / hafifletilebileceğini fark eder ... aslında, "yapılandırılmış programlama" adı verilen "daha güvenli" programlama için her bir işlevin sahip olması gereken bütün bir ilgili strateji vardır. tek bir giriş ve çıkış noktası da (yani gitme, erken dönüş yok). Bazı hataları ortadan kaldırabilir, ancak şüphesiz diğerlerini ortaya çıkarır. Neden yapıyorlar?

  • belirli bir programlama / kod stilini teşvik eden bir geliştirme çerçevesine sahipler ve bunun bu sınırlı çerçevede net bir fayda sağladığına dair istatistiksel kanıtları var veya
  • programlama yönergelerinden veya böyle bir çerçevedeki deneyimden etkilenmişlerse veya
  • onlar sadece diktatörce aptallar veya
  • yukarıdaki + tarihsel ataletlerin herhangi biri (gerekçelerin C için modern C ++ 'dan daha uygulanabilir olmasıyla ilgilidir).

Evet, ama sonra yine dini olarak kaçınmaya çalışan insanların kod hatalarını gördüm break. Bundan kaçındılar, ancak ara kullanımının yerini alan koşullar üzerinde düşünmeyi başaramadılar.
Tomáš Zato -

5

Kullanması tamamen geçerli break- diğerlerinin de belirttiği gibi, aynı ligde hiçbir yerde değil goto.

Bununla birlikte vFound, değerin dizide bulunup bulunmadığını döngü dışında kontrol etmek istediğinizde değişkeni kullanmak isteyebilirsiniz. Aynı zamanda, sürdürülebilirlik açısından, çıkış kriterlerini işaret eden ortak bir bayrağa sahip olmak faydalı olabilir.


5

Şu anda üzerinde çalıştığım kod tabanı üzerinde bazı analizler yaptım (40.000 satır JavaScript).

breakBunlardan sadece 22 ifade buldum :

  • switchİfadelerin içinde 19 kullanıldı (toplamda sadece 3 switch ifademiz var!).
  • 2 fordöngülerin içinde kullanıldı - hemen ayrı işlevlere yeniden düzenlenecek ve returnifadeyle değiştirilecek şekilde sınıflandırdığım bir kod .
  • Son breakwhiledöngüye gelince ... git blameBu saçmalığı kimin yazdığını görmek için koştum !

Benim istatistiklere göre Yani: Eğer breakÖnceden kullanmış dışında switch, bir kod koku olduğunu.

Ayrıca continueifadeleri araştırdım . Hiçbiri bulunamadı.


4

O noktada DURDURMA işlemini tamamlamak istemenizin SAĞLANAN kötü bir uygulama olmasının nedenini göremiyorum.


4

Gömülü dünyada, aşağıdaki yapıyı kullanan birçok kod var:

    while(1)
    { 
         if (RCIF)
           gx();
         if (command_received == command_we_are_waiting_on)
           break;
         else if ((num_attempts > MAX_ATTEMPTS) || (TickGet() - BaseTick > MAX_TIMEOUT))
           return ERROR;
         num_attempts++;
    }
    if (call_some_bool_returning_function())
      return TRUE;
    else
      return FALSE;

Bu çok genel bir örnek, perde arkasında pek çok şey oluyor, özellikle kesiliyor. Bunu standart kod olarak kullanmayın, sadece bir örnek göstermeye çalışıyorum.

Benim kişisel görüşüm, döngüde süresiz olarak kalmayı önlemek için uygun özen gösterildiği sürece bu şekilde bir döngü yazmanın yanlış bir tarafı olmadığıdır.


1
Bunu biraz detaylandırır mısın? Bana öyle geliyor ki döngü kesinlikle hiçbir şey yapmıyor ... continueuygulama mantığında ifadeler yoksa . Varmı?
Rene Saarsoo

1
Devam eden ifadeler zaten sonsuz bir döngü ile bağlı olduğunuz için gereksizdir. Temel olarak bir hedefe ulaşmak için gerekli mantığı gerçekleştirirsiniz ve bu hedef n sayıda denemede karşılanmazsa veya bir zaman aşımı koşulu sona ererse, döngüden bir ara yapı aracılığıyla çıkar ve düzeltici eylemi gerçekleştirir veya bir hatanın çağıran kodunu bildirirsiniz. Bu tür bir kodu genellikle ortak bir veri yolu (RS-485, vb.) Üzerinden iletişim kuran uC'lerde görüyorum Temelde hattı ele geçirmeye ve çarpışma olmadan iletişim kurmaya çalışıyorsunuz, çarpışmalar olursa rastgele bir sayı ile belirlenen bir süre bekler ve denersiniz. tekrar.
Nate

1
Tersine, işler iyi giderse, koşul karşılandığında çıkmak için break ifadesini de kullanırsınız. Bu durumda, döngüyü izleyen kod yürütülür ve herkes mutludur.
Nate

"eğer (call_some_bool_returning_function ()) DOĞRU döndürür; aksi takdirde YANLIŞ döndürür;" sadece "return call_some_bool_returning_function ()" olabilirdi
frankster

3

Kullanım durumunuza bağlıdır. Bir for döngüsünün çalışma zamanının sabit olması gereken uygulamalar vardır (örneğin, bazı zamanlama kısıtlamalarını karşılamak veya veri dahili bilgilerinizi zamanlama tabanlı saldırılardan gizlemek için).

Bu durumlarda, bir bayrak ayarlamak ve yalnızca tüm fordöngü yinelemeleri gerçekten çalıştıktan SONRA bayrak değerini kontrol etmek bile mantıklı olacaktır . Elbette, tüm for döngüsü yinelemelerinin hala aynı süreyi alan kodu çalıştırması gerekir.

Çalışma süresi sizin için önemli değilse ... kodun okunmasını kolaylaştırmak için break;ve continue;kullanın.


1
Zamanlama önemliyse, programın işe yaramaz olduğu bilinen işlemleri yapmasına izin vermektense, biraz uyumak ve sistem kaynaklarından tasarruf etmek daha iyi olabilir.
Bgs

2

On MISRA C dev Şirketim kullanılan 98 kural, break ifadesi kullanılmayacaktır ...

Düzenleme: MISRA '04'te kesintiye izin verilir


2
Bu soruyu tam olarak neden sordum! Böyle bir kuralın kodlama kılavuzu olarak bulunmasının iyi bir nedenini bulamıyorum. Ayrıca buradaki herkesin dediği gibi, kırın; daha iyi görünüyor.
kiki

1
Tam olarak neden yasaklandığını bilmiyorum (benden daha zeki bir kişi bunu düşünüyor ...) ama mola (ve çoklu dönüş) okunabilirlik için daha iyidir (bence, ama benim görüşlerim şirketin kuralları değildir ... )
Benoît

1
MISRA kuralları break ifadelerinin kullanımına izin veriyor: misra.org.uk/forum/viewtopic.php?f=75&t=298
luis.espinal

MISRA 98 kurallarını kullandık ... MISRA 2004'ün kuralları değiştirdiği kesin
Benoît

0

Elbette, break;for döngüsünü veya foreach döngüsünü durdurmanın çözümü. Bunu php'de foreach ve for döngüsünde kullandım ve çalışıyor buldum.


0

Katılmıyorum!

Kendinizinkini oluşturmak için bir for döngüsünün yerleşik işlevini neden göz ardı edesiniz? Tekerleği yeniden icat etmenize gerek yok.

Bence çeklerinizin for döngüsünün en üstünde olması daha mantıklı.

for(int i = 0; i < myCollection.Length && myCollection[i].SomeValue != "Break Condition"; i++)
{
//loop body
}

veya önce satırı işlemeniz gerekiyorsa

for(int i = 0; i < myCollection.Length && (i == 0 ? true : myCollection[i-1].SomeValue != "Break Condition"); i++)
{
//loop body
}

Bu şekilde, her şeyi gerçekleştirmek ve çok daha temiz kod yapmak için bir işlev yazabilirsiniz.

for(int i = 0; i < myCollection.Length && (i == 0 ? true : myCollection[i-1].SomeValue != "Break Condition"); i++)
{
    DoAllThatCrazyStuff(myCollection[i]);
}

Veya durumunuz karmaşıksa, bu kodu da taşıyabilirsiniz!

for(int i = 0; i < myCollection.Length && BreakFunctionCheck(i, myCollection); i++)
{
    DoAllThatCrazyStuff(myCollection[i]);
}

Molalarla dolu "Profesyonel Kod" bana pek profesyonel kod gibi gelmiyor. Tembel kodlamaya benziyor;)


4
tembel kodlama profesyonel koddur :)
Jason

Sadece
kalçada

2
"GTFO ASAP" uygulama eğilimindeyim. Yani, eğer bir yapıdan temiz bir şekilde çıkabiliyorsam, bunu mümkün olan en kısa sürede ve çıkışı belirten koşula yakın bir şekilde yapmalıyım.
Chris Cudmore

Bu da işe yarıyor. Sanırım aşmaya çalıştığım argüman, bir for döngüsü kullanıyorsanız, kodunuzu okunabilir ve modüler hale getirmek için yerleşik işlevselliğini kullanmanın zarar vermeyeceğidir. Döngünüzün içinde kesinlikle break ifadelerine ihtiyacınız varsa, oturup neden bu karmaşıklık düzeyine ihtiyaç duyulduğunu düşünürdüm.
Biff MaGriff

1
Haha. Cesaretini breakkıran çoğu insanın bunun kod okunabilirliği için olduğunu iddia ettiğini düşünün . Şimdi yukarıdaki döngülerdeki çılgın 5 mil uzunluğundaki koşula tekrar bakın ...
Tomáš Zato - Yeniden eski haline getir Monica
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.