Bir anahtarın içinden bir döngüden nasıl çıkılır?


113

Şuna benzeyen bir kod yazıyorum:

while(true) {
    switch(msg->state) {
    case MSGTYPE: // ... 
        break;
    // ... more stuff ...
    case DONE:
        break; // **HERE, I want to break out of the loop itself**
    }
}

Bunu yapmanın doğrudan bir yolu var mı?

Bir bayrak kullanabileceğimi ve anahtardan hemen sonra koşullu bir mola vererek döngüden kopabileceğimi biliyorum. Sadece C ++ 'nın bunun için bir yapıya sahip olup olmadığını bilmek istiyorum.


20
Geçişten sonra neden şartlı bir molaya ihtiyacınız var? Sadece while (doğru) yerine while (bayrak) ...
Tal Pressman

7
@Dave_Jarvis Bunun, ne yapmaya çalıştığını göstermek için buraya koyduğu basitleştirilmiş bir versiyon olduğunu varsayıyorum.
Alterlife

6
Birkaç sayfa uzunluğunda işlevler üreten bu programcılardan gotobiriyseniz, çekici ve bazen de tek temiz çıkış yolu bulacaksınız . Kodunuzu yalnızca birkaç satır uzunluğunda küçük işlevler halinde düzenleme eğilimindeyseniz ve her biri tek bir şey yaparsanız, bu sorunla asla karşılaşmazsınız. (Bu arada kodunuzun da okunması daha kolay olacaktır.)
sbi

13
En yakın metro istasyonuna nasıl gideceğinizi bilmek istediğinizde sigarayı bırakmanız için bir tavsiye almak gibi bir şey.
Michael Krelin - hacker

7
@hacker: Eğer duman yüzünden önünüzdeki metro istasyonunu göremezseniz, bu tavsiye o kadar da kötü olmayabilir. :)
sbi

Yanıtlar:


49

Öncül

Aşağıdaki kod, dil veya istenen işlevsellikten bağımsız olarak kötü form olarak kabul edilmelidir:

while( true ) {
}

Destekleyici Argümanlar

while( true )Döngü bunun nedeni kötü biçimidir:

  • Bir while döngüsünün zımni sözleşmesini bozar.
    • While döngüsü bildirimi, tek çıkış koşulunu açıkça belirtmelidir .
  • Sonsuza dek döndüğünü ima eder.
    • Sonlandırma cümlesini anlamak için döngü içindeki kod okunmalıdır.
    • Sonsuza kadar tekrar eden döngüler, kullanıcının programı program içinden sonlandırmasını engeller.
  • Verimsizdir.
    • "Doğru" nun kontrol edilmesi dahil olmak üzere birden fazla döngü sonlandırma koşulu vardır.
  • Böceklere eğilimlidir.
    • Her yineleme için her zaman çalıştırılacak kodun nereye koyulacağını kolayca belirleyemez.
  • Gereksiz karmaşık koda yol açar.
  • Otomatik kaynak kodu analizi.
    • Hataları bulmak, program karmaşıklığı analizi, güvenlik kontrolleri yapmak veya kod yürütmeden başka herhangi bir kaynak kodu davranışını otomatik olarak türetmek için, ilk kırılma koşullarının belirtilmesi, algoritmaların yararlı değişmezleri belirlemesine olanak tanır ve böylece otomatik kaynak kodu analizi ölçümlerini iyileştirir.
  • Sonsuz döngüler.
    • Herkes while(true)sonsuz olmayan döngüler için her zaman kullanıyorsa , döngüler gerçekte sonlandırma koşulu olmadığında kısaca iletişim kurma yeteneğimizi kaybederiz. (Muhtemelen, bu zaten olmuştur, bu nedenle mesele tartışmalıdır.)

"Git" e alternatif

Aşağıdaki kod daha iyi bir formdur:

while( isValidState() ) {
  execute();
}

bool isValidState() {
  return msg->state != DONE;
}

Avantajları

Bayrak yok. Hayır goto. İstisna yok. Değiştirmesi kolay. Okuması kolay. Düzeltmesi kolay. Ek olarak kod:

  1. Döngünün iş yükü bilgisini döngünün kendisinden ayırır.
  2. Kodu koruyan birinin işlevselliği kolayca genişletmesine izin verir.
  3. Tek bir yerde birden çok sonlandırma koşulunun atanmasına izin verir.
  4. Yürütülecek koddan sonlandırma cümlesini ayırır.
  5. Nükleer Santraller için daha güvenlidir. ;-)

İkinci nokta önemlidir. Kodun nasıl çalıştığını bilmeden, birisi benden ana döngüyü yapmamı isterse, diğer iş parçacıklarının (veya işlemlerin) biraz CPU zamanı olmasına izin vermesi durumunda, akla iki çözüm gelir:

Seçenek 1

Duraklamayı kolayca ekleyin:

while( isValidState() ) {
  execute();
  sleep();
}

Seçenek 2

Yürütmeyi geçersiz kıl:

void execute() {
  super->execute();
  sleep();
}

Bu kod, gömülü bir döngüden daha basittir (dolayısıyla okunması daha kolaydır) switch. isValidStateDöngü sürdürmesi konusunda yöntem yalnızca belirlemelidir. Yöntemin iş gücü, executealt sınıfların varsayılan davranışı geçersiz kılmasına izin veren yönteme soyutlanmalıdır (gömülü switchve kullanan zor bir görev goto).

Python Örneği

StackOverflow'da yayınlanan aşağıdaki yanıtı (bir Python sorusuyla) karşılaştırın:

  1. Sonsuza kadar döngü.
  2. Kullanıcıdan seçimini girmesini isteyin.
  3. Kullanıcının girdisi 'yeniden başlat' ise sonsuza kadar döngüye devam edin.
  4. Aksi takdirde, döngüyü sonsuza kadar durdurun.
  5. Son.
kod
while True: 
    choice = raw_input('What do you want? ')

    if choice == 'restart':
        continue
    else:
        break

print 'Break!' 

Karşı:

  1. Kullanıcının seçimini başlatın.
  2. Kullanıcının seçimi 'yeniden başlatma' iken döngü.
  3. Kullanıcıdan seçimini girmesini isteyin.
  4. Son.
kod
choice = 'restart';

while choice == 'restart': 
    choice = raw_input('What do you want? ')

print 'Break!'

Burada while Trueyanıltıcı ve aşırı karmaşık kodlar ortaya çıkar.


45
while(true)Zararlı olması gereken fikir bana tuhaf geliyor. Yürütülmeden önce kontrol eden döngüler var, daha sonra kontrol edenler var ve ortada kontrol edenler var. İkincisi C ve C ++ 'da sözdizimsel yapıya sahip olmadığından, while(true)veya kullanmanız gerekecek for(;;). Bunu yanlış düşünürseniz, farklı türdeki döngüleri henüz yeterince düşünmemişsinizdir.
sbi

34
Neden katılmadığımın nedenleri: while (true) ve for (;;;) kafa karıştırıcı değiller (do {} while (true) 'dan farklı olarak) çünkü ne yaptıklarını önceden belirtirler. Aslında "break" ifadelerini aramak, rastgele bir döngü koşulunu ayrıştırıp ayarlandığı yeri aramaktan daha kolaydır ve doğruluğunu kanıtlamak daha zor değildir. Kodun nereye koyulacağına ilişkin argüman, bir continue ifadesinin varlığında da aynı derecede zorludur ve if ifadeleriyle daha iyi değildir. Verimlilik argümanı kesinlikle aptalca.
David Thornley

23
"While (true)" ifadesini gördüğünüzde, rastgele bitiş koşullarından daha zor olmayan dahili sonlandırma koşullarına sahip bir döngü düşünebilirsiniz. Basitçe, "while (foo)" döngüsü, burada foo keyfi bir şekilde ayarlanmış bir boole değişkendir, kesmelerle "while (doğru)" dan daha iyi değildir. Her iki durumda da koda bakmanız gerekir. Aptalca bir neden ileri sürmek (ve mikro etkinlik aptalca bir argümandır) bu arada davanıza yardımcı olmaz.
David Thornley

5
@Dave: Bir sebep verdim: Ortadaki durumu kontrol eden bir döngü elde etmenin tek yolu bu. Klasik örnek: while(true) {string line; std::getline(is,line); if(!is) break; lines.push_back(line);}Elbette bunu önceden koşullandırılmış bir döngüye dönüştürebilirim ( std::getlinedöngü koşulu olarak kullanarak ), ancak bunun kendi başına dezavantajları vardır ( linedöngünün kapsamı dışında bir değişken olması gerekir). Ve eğer yapmış olsaydım, sormam gerekirdi: Neden yine de üç döngümüz var? Her şeyi her zaman önceden koşullandırılmış bir döngüye dönüştürebiliriz. En uygun olanı kullanın. Uygunsa while(true), kullanın.
sbi

3
Bu cevabı okuyan insanlara nezaket göstererek, soruyu üreten malın kalitesi hakkında yorum yapmaktan kaçınır ve soruyu cevaplamaktan vazgeçerdim. Soru, aşağıdakilerin bir çeşididir ve birçok dilin özelliği olduğuna inanıyorum: başka bir döngünün içinde yuvalanmış bir döngünüz var. İç döngüden dış döngüden çıkmak istiyorsunuz. Bu nasıl yapılır?
Alex Leibowitz

172

Kullanabilirsiniz goto.

while ( ... ) {
   switch( ... ) {
     case ...:
         goto exit_loop;

   }
}
exit_loop: ;

12
Sadece bu anahtar kelimeyle vahşileşmeyin.
Fragsworth

45
İnsanlar hoşlanmadığı için olumsuz oy almam komik goto. OP , bayrak kullanmadan açıkça belirtilmiştir . OP'nin sınırlamasını göz önünde bulundurarak daha iyi bir yol önerebilir misiniz? :)
Mehrdad Afshari

18
Sadece akılsız gitmekten nefret edenleri telafi etmek için oy verildi. Sanırım Mehrdad, burada mantıklı bir çözüm önerdiği için birkaç puan kaybedeceğini biliyordu.
Michael Krelin - hacker

6
+1, OP'nin sınırlamaları düşünüldüğünde bile daha iyi bir yol yok. Bayraklar çirkin, tüm döngü boyunca mantığı bozuyor ve bu nedenle kavramak daha zor.
Johannes Schaub - litb

11
Günün sonunda, goto yapısı olmadan tüm diller başarısız olur. yüksek seviyeli diller bunu gizler, ancak yeterince dikkatli bakarsanız, her döngüyü veya koşulun koşullu ve koşulsuz dallara dönüştüğünü göreceksiniz. for, while, until, switch, if anahtar sözcüklerini kullanarak kendimizi kandırırız, ancak sonunda hepsi bir şekilde GOTO (veya JMP) kullanır. Kendinizi kandırabilir, gizlemek için her türlü zekice yöntem icat edebilirsiniz ya da sadece pragmatik olarak dürüst olabilir ve uygun olduğu yerde ve tedbirli bir şekilde goto kullanabilirsiniz.
senkronize edilmemiş

58

Alternatif bir çözüm, anahtar kelimeyi aşağıdakilerle continuekombinasyon halinde kullanmaktır break, yani:

for (;;) {
    switch(msg->state) {
    case MSGTYPE
        // code
        continue; // continue with loop
    case DONE:
        break;
    }
    break;
}

continueDöngünün devam etmesini istediğiniz her durum etiketini bitirmek için deyimi kullanın ve döngüyü breaksonlandırması gereken büyük / küçük harf etiketlerini bitirmek için deyimi kullanın.

Elbette bu çözüm yalnızca switch ifadesinden sonra yürütülecek ek kod yoksa işe yarar.


9
Bu gerçekten çok zarif olsa da, çoğu geliştiricinin nasıl çalıştığını / çalışıp çalışmadığını anlamak için bir dakikalığına ona bakmak zorunda olması dezavantajına sahiptir. :(
sbi

8
Son cümle, bunu o kadar da harika yapan şeydir :(. Aksi takdirde çok temiz bir uygulama.
nawfal

Düşündüğünüzde, hiçbir bilgisayar kodu iyi değildir. Biz sadece onu anlamak için eğitildik. Örneğin, xml öğrenene kadar çöp gibi görünüyordu. Artık daha az çöp görünüyor. for (int x = 0; x <10; ++ x, moveCursor (x, y)) aslında programımda bir mantık hatasına neden oldu. Güncelleme koşulunun sonunda olduğunu unuttum.

22

Bunu yapmanın düzgün bir yolu, bunu bir işleve sokmak olabilir:

int yourfunc() {

    while(true) {

        switch(msg->state) {
        case MSGTYPE: // ... 
            break;
        // ... more stuff ...
        case DONE:
            return; 
        }

    }
}

İsteğe bağlı olarak (ancak 'kötü uygulamalar'): daha önce önerildiği gibi, bir goto kullanabilir veya anahtarın içine bir istisna atabilirsiniz.


4
İstisna, bir fonksiyonun iyi bilinen bir davranışını değil, bir istisna atmak için kullanılmalıdır.
Clement Herreman

Afterlife'a katılıyorum: Bir işleve koyun.
dalle

14
Bu durumda bir istisna yapmak gerçekten kötüdür. "Bir goto kullanmak istiyorum, ama onları kullanmamam gerektiğini bir yerde okudum, bu yüzden bir hile yapacağım ve akıllı görünüyormuş gibi yapacağım" anlamına geliyor.
Gorpik

Bir istisna atmanın veya bir goto kullanmanın korkunç bir fikir olduğunu kabul ediyorum, ancak bunlar işe yarayan seçenekler ve cevabımda bu şekilde listelenmiş durumda.
Alterlife

2
Bir istisna atmak, bir GOTO yerine COMEFROM kullanmaktır. Yapma. Goto çok daha iyi çalışıyor.
David Thornley

14

AFAIK, C ++ 'da "çift kesme" veya benzeri bir yapı yoktur. En yakın olanı goto- ismine kötü bir çağrışım olsa da, dilde bir sebepten dolayı var olan - dikkatli ve idareli kullanıldığı sürece geçerli bir seçenek olacaktır.


8

Anahtarınızı aşağıdaki gibi ayrı bir işleve koyabilirsiniz:

bool myswitchfunction()
{
    switch(msg->state) {
    case MSGTYPE: // ... 
        break;
    // ... more stuff ...
    case DONE:
        return false; // **HERE, I want to break out of the loop itself**
    }
    return true;
}

while(myswitchfunction())
    ;

7

Gitmeyi sevmeseniz bile, döngüden çıkmak için bir istisna kullanmayın. Aşağıdaki örnek bunun ne kadar çirkin olabileceğini göstermektedir:

try {
  while ( ... ) {
    switch( ... ) {
      case ...:
        throw 777; // I'm afraid of goto
     }
  }
}
catch ( int )
{
}

Bu cevapta gotoolduğu gibi kullanırdım . Bu durumda , kodu diğer seçeneklerden daha net hale getirecektir. Umarım bu soru yardımcı olur.goto

Ama bence gotodizeden dolayı buradaki tek seçenek kullanmak while(true). Döngünüzü yeniden düzenlemeyi düşünmelisiniz. Sanırım aşağıdaki çözüm:

bool end_loop = false;
while ( !end_loop ) {
    switch( msg->state ) {
    case MSGTYPE: // ... 
        break;
    // ... more stuff ...
    case DONE:
        end_loop = true; break;
    }
}

Ya da aşağıdakileri bile:

while ( msg->state != DONE ) {
    switch( msg->state ) {
    case MSGTYPE: // ... 
        break;
    // ... more stuff ...
}

1
Evet, ama ben istisnalar gibi daha az ;-)
quamrana

3
Bir gotoyu taklit etmek için bir istisna kullanmak, aslında bir goto kullanmaktan daha kötüdür! Muhtemelen önemli ölçüde daha yavaş olacaktır.
John Carter

2
@Downvoter: Bunun nedeni, istisna kullanmamanız gerektiğini belirttiğim için mi yoksa yeniden düzenleme yapmayı önerdiğim için mi?
Kirill V. Lyadvinsky

1
Aşağı oy veren değil - ama ... Döngüden çıkmak için bir istisna kullanma fikrini öneriyor musunuz yoksa kınıyor musunuz? Cevabınız net değil. Belki ilk cümle olmalıdır: "değil gibi yapsan bile goto, do not bir döngü çıkmak için bir istisna kullanın:"?
Jonathan Leffler

1
@Jonathan Leffler, Teşekkürler, değerli yorum örneğini gösterdin. yorumlarınızı dikkate alarak cevabı güncelledi.
Kirill V. Lyadvinsky

5

Bu durumda döngüden çıkmak için C ++ yapısı yoktur.

Döngüyü kesmek için bir bayrak kullanın veya (uygunsa) kodunuzu bir işleve çıkarın ve kullanın return.


2

Potansiyel olarak goto kullanabilirsiniz, ancak döngüyü durduran bir bayrak ayarlamayı tercih ederim. Sonra anahtardan çıkın.


2

Neden sadece while döngünüzdeki koşulu düzeltip sorunun ortadan kalkmasına neden olmuyorsunuz?

while(msg->state != DONE)
{
    switch(msg->state) {
    case MSGTYPE: // ... 
        break;
    // ... more stuff ...
    case DONE:
        // We can't get here, but for completeness we list it.
        break; // **HERE, I want to break out of the loop itself**
    }
}

2

Hayır, "break" anahtar sözcüğü zaten anahtar bloğundan çıkmak için ayrılmış olduğundan, C ++ bunun için bir yapıya sahip değildir. Alternatif olarak, çıkış bayrağına sahip bir do.. while () yeterli olabilir.

do { 
    switch(option){
        case 1: ..; break;
        ...
        case n: .. ;break;
        default: flag = false; break;
    }
} while(flag);

1

Bence;

while(msg->state != mExit) 
{
    switch(msg->state) 
    {
      case MSGTYPE: // ...
         break;
      case DONE:
      //  .. 
      //  ..
      msg->state =mExit;
      break;
    }
}
if (msg->state ==mExit)
     msg->state =DONE;

0

Bunu yapmanın en basit yolu, ANAHTARı yapmadan önce basit bir EĞER koymak ve bu EĞER döngüden çıkmak için koşulunuzu test etmektir .......... olabildiğince basit


2
Um ... while argümanına bu koşulu koyarak daha da basitleşebilirsin. Demek istediğim, bunun için var! :)
Mark A. Donohoe

0

breakC ++ anahtar kelime sadece en iç içe kapsayan yineleme veya sonlandırır switchdeyimi. Böylece,while (true) doğrudan ifade içinde döngüden switchçıkamazsınız; bununla birlikte, bu tür problemler için mükemmel bir model olduğunu düşündüğüm aşağıdaki kodu kullanabilirsiniz:

for (; msg->state != DONE; msg = next_message()) {
    switch (msg->state) {
    case MSGTYPE:
        //...
        break;

    //...
    }
}

msg->stateEşit olduğunda bir şey yapmanız gerekiyorsaDONE (temizleme rutini çalıştırmak gibi), o kodu fordöngüden hemen sonra yerleştirin ; yani şu anda sahipseniz:

while (true) {
    switch (msg->state) {
    case MSGTYPE:
        //... 
        break;

    //...

    case DONE:
        do_cleanup();
        break;
    }

    if (msg->state == DONE)
        break;

    msg = next_message();
}

O zaman bunun yerine şunu kullanın:

for (; msg->state != DONE; msg = next_message()) {
    switch (msg->state) {
    case MSGTYPE:
        //...
        break;

    //...
    }
}

assert(msg->state == DONE);
do_cleanup();

0

Açıklamaların derinliği düşünüldüğünde bunun ne kadar basit olması beni şaşırtıyor ... İşte ihtiyacınız olan her şey ...

bool imLoopin = true;

while(imLoopin) {

    switch(msg->state) {

        case MSGTYPE: // ... 
            break;

        // ... more stuff ...

        case DONE:
            imLoopin = false;
            break;

    }

}

LOL !! Gerçekten mi! Tek ihtiyacın olan bu! Ekstra bir değişken!


0
while(MyCondition) {
switch(msg->state) {
case MSGTYPE: // ... 
    break;
// ... more stuff ...
case DONE:
   MyCondition=false; // just add this code and you will be out of loop.
    break; // **HERE, you want to break out of the loop itself**
}
}

0

Aynı sorunu yaşadım ve bir bayrak kullanarak çözdüm.

bool flag = false;
while(true) {
    switch(msg->state) {
    case MSGTYPE: // ... 
        break;
    // ... more stuff ...
    case DONE:
        flag = true; // **HERE, I want to break out of the loop itself**
    }
    if(flag) break;
}

-2

C ++ sözdizimini iyi hatırlıyorsam break, için olduğu gibi ifadelere bir etiket ekleyebilirsiniz goto. Yani istediğiniz şey kolayca yazılabilir:

while(true) {
    switch(msg->state) {
    case MSGTYPE: // ...
        break;
    // ... more stuff ...
    case DONE:
        break outofloop; // **HERE, I want to break out of the loop itself**
    }
}

outofloop:
// rest of your code here

1
Maalesef hafızanız hatalı. Yararlı olacağı ender durumlarda güzel bir özellik olurdu. (Ben kopmaya düzeylerinin sayısı için öneriler gördüm ama böcek gibi bana bu bakış olmayı bekleyen.)
David Thornley

O zaman Java olmalı. Cevapladığınız için teşekkürler. Ve tabii ki geri kalanında da haklısın.
Andrei Vajna II

-3
  while(true)
  {
    switch(x)
    {
     case 1:
     {
      break;
     }
    break;
   case 2:
    //some code here
   break;
  default:
  //some code here
  }
}

1
Bu kod örneğindeki tüm kesmeler switch deyiminden çıkar.
Dan Hulme
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.