Birden fazla iç içe 'döngülerden çıkmak için break kullanabilir miyim?


305

Bu breakişlevi birkaç iç içe fordöngüden çıkmak için kullanmak mümkün mü ?

Eğer öyleyse, bunu nasıl yapardınız? breakÇıkışların kaç döngüsünü de kontrol edebilir misiniz ?


1
Birden çok iç içe döngüden çıkmak için break veya goto kullanmak yerine, belirli bir mantığı bir işleve dahil edebilir ve birden çok iç içe döngüden çıkmak için return özelliğini kullanabilirsiniz. Bu, kodunuzun estetiğini koruyacak ve kötü bir programlama uygulaması olan goto'yu kullanmanızı engelleyecektir.
Rishab Shinghal

Yanıtlar:


239

AFAIK, C ++, Java ve diğer diller gibi adlandırma döngülerini desteklemez. Bir goto kullanabilir veya kullandığınız bir bayrak değeri oluşturabilirsiniz. Her döngünün sonunda bayrak değerini kontrol edin. Eğer true olarak ayarlanırsa, o yinelemeden kurtulabilirsiniz.


317
En gotoiyi seçenek bu ise kullanmaktan korkmayın .
jkeys

18
Ben yeni bir C ++ programcısıyım (ve herhangi bir resmi programlama eğitimi olmayan biriyim), böylece insanların goto'daki sıralarını okuduktan sonra. Programımın aniden patlayıp beni öldürebileceği korkusuyla kullanmakta tereddüt ediyorum. Bunun dışında, ti-83 (tabii ki sıkıcı matematik dersinde) programları yazmak için kullandığımda, temel düzenleyicinin sağladığı işlevler goto'nun kullanımını gerektiriyordu.
Faken

26
@Faken: İki tür programcı kullanır goto: Kötü programcılar ve pragmatik programcılar. Birincisi açıklayıcıdır. İkincisi, eğer onları iyi kullanmayı seçerseniz, (iki) kötülükten daha az olduğunda “kötü” olarak adlandırılan bir kavram kullanın. Zaman zaman kullanmanız gerekebilecek bazı C ++ kavramlarını daha iyi anlamak için bunu okuyun (makrolar, goto'lar
jkeys

41
@Faken: Gitmekle ilgili yanlış bir şey yok. Sorun yaratan bir goto'yu yanlış kullanıyor.
Herkes

28
@Hooked: Bu doğru, gotonadiren hiç kullanmanın en iyi seçenek olması dışında. Neden döngüleri kendi işlevlerine sokmuyorsunuz ( inlinehız konusunda endişeliyseniz) ve returnbundan?
sbi

265

Hayır, ile şımartmayın break. Bu, kullanımı için kalan son kaledir goto.


19
MISRA C ++ kodlama standardı, bu tür bir durumu kapsaması için goto kullanımına izin verir.
Richard Corden

11
Gerçek programcılar goto kullanmaktan korkmazlar. 25 yıl boyunca bitti - pişmanlık yok - bir ton geliştirme süresi kaydetti.
Doug Null

1
Katılıyorum. gotos, 8K pikseli yoksa ve 30 Windows işletim sistemi çağrısını çağırmanız gerekiyorsa bir zorunluluktur.
Michaël Roy

Veya kodu bir işleve koyarak basit bir "dönüş".
Tara

68

Lambdas kullanarak açık bir cevap eklemek için:

  for (int i = 0; i < n1; ++i) {
    [&] {
      for (int j = 0; j < n2; ++j) {
        for (int k = 0; k < n3; ++k) {
          return; // yay we're breaking out of 2 loops here
        }
      }
    }();
  }

Tabii ki bu model belirli bir sınırlamaya ve sadece C ++ 11'e sahip ancak bence oldukça kullanışlı.


7
Bu, okuyucunun geri dönüşün lambda'nın değil, lambda'nın geri döndüğü işlevin neden olduğunu düşünmesini karıştırır.
Xunie

3
@Xunie: Döngünüz o kadar karmaşıksa, bir lambdada olduğunuzu unutursunuz, onları farklı bir işleve koymanın zamanı geldi, ancak basit durumlar için bu oldukça iyi çalışmalı.
MikeMB

17
Bence bu çözüm güzel
tuket

Lambda'yı bir ad veren ve dtor'da (aka kapsam gaurd) çağıran bir RIIA şablonunda yakalayabilirsiniz. Bu, herhangi bir performans kazancı eklemez, ancak okunabilirliği artırabilir ve işlev çağrısı parantezini kaçırma riskini azaltabilir.
Kırmızı.

56

Yuvalanmış bir döngüden kurtulmak için başka bir yaklaşım, her iki döngüyü de ayrı bir işleve ve returnçıkmak istediğinizde bu işlevden ayırmaktır .

Tabii ki, bu return, sonunda başka bir yerde açıkça bir işlevden hiç çıkıp çıkmayacağınıza dair diğer argümanı gündeme getiriyor .


7
Bu bir C sorunu. RIAA ile erken dönüş bir sorun değildir çünkü erken dönüş ile ilgili tüm sorunlar doğru bir şekilde ele alınmaktadır.
Martin York

4
RIAA'nın doğru uygulanmasının C ++ 'da kaynak temizleme sorununu çözebileceğini anlıyorum, ancak erken dönüşe karşı felsefi argümanın diğer ortamlarda ve dillerde devam ettiğini gördüm. Kodlama standardının erken dönüşü yasakladığı yerlerde çalıştığım bir sistem continue_processing, kod bloklarının işlevde daha aşağı yürütülmesini kontrol eden boolean değişkenlerle (adlar gibi ) çevrilmiş işlevlere sahipti .
Greg Hewgill

21
RIAA nedir? RAII gibi bir şey mi? = D
jkeys

1
Döngüler için kaç tane olduğuna ve yuvanın ne kadar derine gittiğine bağlı ... mavi hapı mı yoksa kırmızı hapı mı istiyorsun?
Matt

34

break yalnızca onu içeren en içteki döngüden çıkar.

İstediğiniz sayıda döngüden kurtulmak için goto komutunu kullanabilirsiniz .

Tabii ki goto genellikle Zararlı olarak kabul edilir .

kesme fonksiyonunu kullanmak doğru mudur [...]?

Break ve goto komutlarını kullanmak, bir programın doğruluğu hakkında mantık yürütmeyi zorlaştırabilir. Bununla ilgili bir tartışma için buraya bakın: Dijkstra deli değildi .


16
İyi bir cevap, "goto zararlı" memenin daha genelleştirilmiş "kontrol akışının kesintiye uğraması zararlı" ifadesine güçlü bir şekilde bağlı olduğunu açıklar. "Git zararlıdır" demek anlamsızdır, sonra arkanı dönüp breakveya kullanmanızı öneririz return.
Pavel Minaev

6
@Pavel: breakve nereye gittiklerini bulmak için bir etiket aramanıza gerek kalmaması returnavantajına gotosahip olun. Evet, altında bir çeşit gotoama çok kısıtlı. Bir programcının kalıp eşleme beyninin kısıtlamasından daha kolay deşifre edilirler goto. IMO tercih edilir.
sbi

@sbi: Doğru, ancak ara yine de yapılandırılmış programlamanın bir parçası değil. A'dan daha iyi tolere edilir goto.
jkeys

2
@KarlVoigtland Dijkstra bağlantısı eskidir; Bu çalışıyor gibi görünüyor: blog.plover.com/2009/07
Aaron Brager

3
Bu durumda goto kullanmanın kesinlikle yanlış bir yanı yoktur. İyi yerleştirilmiş bir goto, başka türlü önerilen çarpık çözeltilerin birçoğundan daha iyi ve daha okunabilir bir şekilde sıçrama yapar.
James

22

Bu cevap zaten sunulmuş olmasına rağmen, iyi bir yaklaşımın aşağıdakileri yapmak olduğunu düşünüyorum:

for(unsigned int z = 0; z < z_max; z++)
{
    bool gotoMainLoop = false;
    for(unsigned int y = 0; y < y_max && !gotoMainLoop; y++)
    {
        for(unsigned int x = 0; x < x_max && !gotoMainLoop; x++)
        {
                          //do your stuff
                          if(condition)
                            gotoMainLoop = true;
        }
    }

}

5
hangi iyi ama yine de okunabilir değil, ben bu durumda goto tercih ederim
Петър Петров

2
Bu, kodunuzu "oldukça" yavaş hale getirir çünkü gotoMainLoopher döngüde kontrol edilir
Thomas

1
Bu durumda, gerçek kullanımı gotoçekirdeği daha okunabilir ve daha iyi performans gösterir.
Петър Петров

20

Buna ne dersin?

for(unsigned int i=0; i < 50; i++)
{
    for(unsigned int j=0; j < 50; j++)
    {
        for(unsigned int k=0; k < 50; k++)
        {
            //Some statement
            if (condition)
            {
                j=50;
                k=50;
            }
        }
    }
}

2
ilginç bir yaklaşım ama ered @ inf.ig.sh tarafından ele alınmasını kesinlikle seviyorum. for (unsigned int y = 0; y <y_max &&! gotoMainLoop; y ++).
Andy

15

gotoYuvalanmış bir döngüden çıkmak için kod örneği ve etiket:

for (;;)
  for (;;)
    goto theEnd;
theEnd:

11

Birkaç iç içe döngüden kurtulmanın güzel bir yolu, kodunuzu bir işleve yeniden düzenlemektir:

void foo()
{
    for(unsigned int i=0; i < 50; i++)
    {
        for(unsigned int j=0; j < 50; j++)
        {
            for(unsigned int k=0; k < 50; k++)
            {
                // If condition is true
                return;
            }
        }
    }
}

4
... bu işlevi yığın çerçeveleme için 10-20 değişken geçirmemiz gerekiyorsa bu bir seçenek değildir.
Петър Петров

1
@ ПетърПетров sonra tam olarak ihtiyacınız olan yeri tanımlayabileceğiniz gibi daha iyi bir lambda için gidin.
DarioP

Lambdas için +1 ama bir yığın çerçevenin bile hala bir darboğaz olduğu oyun motoru çekirdeğinde revizyon. Söylemek üzgünüm, ancak lambdas en azından MSVC 2010'da çok hafif değil.
Петър Петров

@ ПетърПетров Daha sonra işlev çiftini bir sınıfa ve yığın değişkenlerini özel üyelere değiştirin.
Arthur Tacca

Bu sadece zaten karmaşık kodu aşırı karmaşık :) Bazen, goto tek çözümdür. Veya "goto state X" belgeleriyle karmaşık otomata yazarsanız, goto aslında kodun belgede yazıldığı gibi okunmasını sağlar. Ayrıca, C # ve go'nun her ikisinin de bir amacı vardır: hiçbir dil bir goto olmadan turing-complete değildir ve gotos genellikle ara çevirmen veya montaj benzeri kod yazmak için en iyi kullanılan araçlardır.
Петър Петров

5

goto yuvalanmış halkaları kırmak için çok yararlı olabilir

for (i = 0; i < 1000; i++) {
    for (j = 0; j < 1000; j++) {
        for (k = 0; k < 1000; k++) {
             for (l = 0; l < 1000; l++){
                ....
                if (condition)
                    goto break_me_here;
                ....
            }
        }
    }
}

break_me_here:
// Statements to be executed after code breaks at if condition

3

breakİfadesi en yakın kapsayan yürütülmesini durdurur do, for, switchya whilegöründüğü açıklamada. Denetim, sonlandırılmış ifadeyi izleyen ifadeye geçer.

dan msdn .


3

Ben a gotobu durumda geçerli olduğunu düşünüyorum :

Bir simüle etmek break/ continue, istediğiniz ediyorum:

mola

for ( ;  ;  ) {
    for ( ;  ;  ) {
        /*Code here*/
        if (condition) {
            goto theEnd;
        }
    }
}
theEnd:

Devam et

for ( ;  ; ) {
    for ( ;  ;  ) {
        /*Code here*/
        if (condition) {
            i++;
            goto multiCont;
        }
    }
    multiCont:
}

"Devam" burada çalışmaz, çünkü ilk döngünün yineleme ifadesi yürütülmez
Fabio A.

İlk döngü için yineleyici olduğunu varsayıyorum i. Bu i++yüzden gitmeden önce
JuliusAlphonso

0

PHP gibi diğer diller, kırmak istediğiniz iç içe döngü düzeylerinin miktarını belirtmek için break (yani break 2;) parametresini kabul eder, ancak C ++ kabul etmez. Döngüden önce false olarak ayarladığınız, kırmak istiyorsanız döngüde true olarak ayarlanan bir boolean ve ayrıca iç içe döngüden sonra koşullu bir break kullanarak, boolean'ın true olarak ayarlanıp ayarlanmadığını kontrol ederek bir boolean kullanarak çalışmanız gerekecektir. ve evet ise kır.


0

Bunun eski bir gönderi olduğunu biliyorum. Ama biraz mantıklı ve basit bir cevap öneririm.

for(unsigned int i=0; i < 50; i++)
    {
        for(unsigned int j=0; j < conditionj; j++)
        {
            for(unsigned int k=0; k< conditionk ; k++)
            {
                // If condition is true

                j= conditionj;
               break;
            }
        }
    }

3
Bunun yerine çok ölçeklenebilir bir çözüm j = conditionjdeğil, karmaşık bir yükleminiz varsa işe yaramaz j < conditionj.
Sergey

0

Herhangi bir sayıda döngüyü yalnızca bir booldeğişkenle kesin.

bool check = true;

for (unsigned int i = 0; i < 50; i++)
{
    for (unsigned int j = 0; j < 50; j++)
    {
        for (unsigned int k = 0; k < 50; k++)
        {
            //Some statement
            if (condition)
            {
                check = false;
                break;
            }
        }
        if (!check)
        {
            break;
        }
    }
    if (!check)
    {
        break;
    }
}

Bu kodda hepimiz break;döngüleriz.


0

Buna değip değmediğinden emin değilim, ancak Java'nın adlandırılmış döngülerini birkaç basit makro ile taklit edebilirsiniz:

#define LOOP_NAME(name) \
    if ([[maybe_unused]] constexpr bool _namedloop_InvalidBreakOrContinue = false) \
    { \
        [[maybe_unused]] CAT(_namedloop_break_,name): break; \
        [[maybe_unused]] CAT(_namedloop_continue_,name): continue; \
    } \
    else

#define BREAK(name) goto CAT(_namedloop_break_,name)
#define CONTINUE(name) goto CAT(_namedloop_continue_,name)

#define CAT(x,y) CAT_(x,y)
#define CAT_(x,y) x##y

Örnek kullanım:

#include <iostream>

int main()
{
    // Prints:
    // 0 0
    // 0 1
    // 0 2
    // 1 0
    // 1 1

    for (int i = 0; i < 3; i++) LOOP_NAME(foo)
    {
        for (int j = 0; j < 3; j++)
        {
            std::cout << i << ' ' << j << '\n';
            if (i == 1 && j == 1)
                BREAK(foo);
        }
    }
}

Başka bir örnek:

#include <iostream>

int main()
{
    // Prints: 
    // 0
    // 1
    // 0
    // 1
    // 0
    // 1

    int count = 3;
    do LOOP_NAME(foo)
    {
        for (int j = 0; j < 3; j++)
        {
            std::cout << ' ' << j << '\n';
            if (j == 1)
                CONTINUE(foo);
        }
    }
    while(count-- > 1);
}

-1
  while (i<n) {
    bool shouldBreakOuter = false;
    for (int j=i + 1; j<n; ++j) {
      if (someCondition) {
          shouldBreakOuter = true;
      }
    }

    if (shouldBreakOuter == true)
      break;

  }

-3

Deneyin ... yakalamak kullanabilirsiniz.

try {
    for(int i=0; i<10; ++i) {
        for(int j=0; j<10; ++j) {
            if(i*j == 42)
                throw 0; // this is something like "break 2"
        }
    }
}
catch(int e) {} // just do nothing
// just continue with other code

Aynı anda birkaç döngüden kurtulmanız gerekiyorsa, bu genellikle bir istisnadır.


1
Bu cevabın neden bu kadar aşağı oy kullanmasının nedenini bilmek istiyorum.
hkBattousai

6
@hkBattousai Çözüm, oylama akışını denetlemek için bir istisna kullandığından çözümün oyu yok. Adından da anlaşılacağı gibi, istisnalar yalnızca istisnai durumlarda kullanılmalıdır.
Helio Santos

4
@HelioSantos Bu, dilin uygun bir çözüm sağlamadığı istisnai bir durum değil mi?
hkBattousai

8
İstisnalar yavaş.
Gordon

2
Atışın performans etkisi,% 99 oranında kurtarılamaz bir hata olmayan bir şey için çok büyük.
Michaël Roy

-4

Bir for-loop'tan ayrılmak benim için biraz garip, çünkü for-loop'un semantiği tipik olarak belirtilen sayıda çalışacağını gösterir. Ancak, her durumda kötü değildir; bir koleksiyonda bir şey arıyorsanız ve onu bulduktan sonra kırmak istiyorsanız, faydalıdır. İç içe döngülerden kurtulmak, C ++ ile mümkün değildir; etiketli bir mola ile diğer dillerde. Bir etiket ve bir go kullanabilirsiniz, ancak bu size gece mide ekşimesi verebilir ..? Yine de en iyi seçenek gibi görünüyor.


11
Hiç garip değil. Bir şeyi aramak için (ve daha hızlı bir arama yönteminiz yoksa) bir koleksiyonu tekrarlıyorsanız, döngüyü bitirmenin bir anlamı yoktur. (örnek olarak)
Joe
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.