İç içe bir döngüden kopma


216

Başka bir içine yerleştirilmiş bir for döngüsü varsa, her iki döngüden (iç ve dış) mümkün olan en hızlı şekilde nasıl verimli bir şekilde çıkabilirim?

Bir boolean kullanmak ve sonra başka bir yönteme gitmek söylemek zorunda değil, sadece dış döngü sonra kodun ilk satırını yürütmek istemiyorum.

Bunu yapmanın hızlı ve güzel bir yolu nedir?

İstisnaların ucuz olmadığını / sadece istisnai bir durumda vb. Atılması gerektiğini düşünüyordum. Dolayısıyla bu çözümün performans açısından iyi olacağını düşünmüyorum.

Ben oldukça temel bir şey yapmak için .NET (anon yöntemleri) yeni özelliklerinden yararlanmak doğru olduğunu düşünmüyorum.


Sadece emin olmak istedim: bunu neden yapmak istiyorsun?
Jon Limjap

3
Neden bir boole kullanmak istemiyorsun? Bunu yapmanın nesi yanlış?
Anthony

VB.net'te bir try / finally deyimini (catch yok) rastgele sayıda döngüye sarabilirsiniz, sonra "exit try" herhangi bir noktada çıkacaktır.
Brain2000

Yanıtlar:


206

Şey, gotoama bu çirkin ve her zaman mümkün değil. Ayrıca döngüleri bir yönteme (veya anon yöntemine) yerleştirebilir ve returnana koda geri dönmek için kullanabilirsiniz .

    // goto
    for (int i = 0; i < 100; i++)
    {
        for (int j = 0; j < 100; j++)
        {
            goto Foo; // yeuck!
        }
    }
Foo:
    Console.WriteLine("Hi");

vs:

// anon-method
Action work = delegate
{
    for (int x = 0; x < 100; x++)
    {
        for (int y = 0; y < 100; y++)
        {
            return; // exits anon-method
        }
    }
};
work(); // execute anon-method
Console.WriteLine("Hi");

C # 7'de (yerel sözdizimi) elde etmemiz gerektiğini unutmayın;

// local function (declared **inside** another method)
void Work()
{
    for (int x = 0; x < 100; x++)
    {
        for (int y = 0; y < 100; y++)
        {
            return; // exits local function
        }
    }
};
Work(); // execute local function
Console.WriteLine("Hi");

49
Bu tür bir durumda, goto kullanmanın break gibi bir şeyin normal kullanımından daha kötü olduğunu düşünmüyorum (sonuçta ikisi de bir etikete sadece koşulsuz dallar, sadece etiketin kırılmasıyla örtülüdür).
Greg Beech

37
bazen alternatifler daha az kötüdür
seanb

7
@BeowulfOF - kopma sadece iç döngüden kopar, iç ve dış döngülerden değil.
Greg Beech

36
Git çirkin değil. Çirkin olan, spagetti koduyla sonuçlanan goto'yu kötüye kullanmaktır. Yuvalanmış döngüden kurtulmak için goto kullanımı mükemmel bir şekilde tamam. Ayrıca, tüm kırılma, devam etme ve geri dönüşün, yapısal programlama bakış açısından, goto'dan çok daha iyi olmadığını unutmayın - temelde aynı şey, sadece daha güzel ambalajlarda. Bu yüzden saf yapısal diller (orijinal Pascal gibi) üçünden de yoksundur.
el.pescado

11
@ el.pescado tamamen haklı: Birçok kodlayıcılar inanmak yanlış yönlendiren olmuştur gotozararlıdır başına se basitçe iken, doğru bazı şeyler yapmak enstrüman ve birçok enstrümanlar gibi suiistimal edilebilirler. Bu dini isteksizlik gotoaçıkçası oldukça aptalca ve kesinlikle bilimsel değil.
o0 '.

96

Dış döngü değişkeninin döngü koşullarının dışındaki C - set değerinde sıklıkla kullanılan yaklaşımın C # uyarlaması (yani int değişkenini kullanan döngü INT_MAX -1için genellikle iyi bir seçimdir):

for (int i = 0; i < 100; i++)
{
    for (int j = 0; j < 100; j++)
    {
        if (exit_condition)
        {
            // cause the outer loop to break:
            // use i = INT_MAX - 1; otherwise i++ == INT_MIN < 100 and loop will continue 
            i = int.MaxValue - 1;
            Console.WriteLine("Hi");
            // break the inner loop
            break;
        }
    }
    // if you have code in outer loop it will execute after break from inner loop    
}

Kodda notun dediği gibi breaksihirli bir şekilde dış halkanın bir sonraki yinelemesine atlamayacaktır - bu yüzden iç halkanın dışında kodunuz varsa bu yaklaşım daha fazla kontrol gerektirir. Bu durumda diğer çözümleri düşünün.

Bu yaklaşım ile çalışır forve whiledöngüler ancak işe yaramaz foreach. Durumunda foreach(eğer olabilseydi bile ve bunu değiştiremezsiniz böylece size gizli numaralandırıcıya kod erişemez IEnumeratorbazı "MoveToEnd" yöntemine sahip değildir).

İnlined comments' yazarlara Teşekkür:
i = INT_MAX - 1tarafından öneri Meta
for / foreachtarafından comment ygoe .
Uygun IntMaxtarafından jmbpiano
ile iç döngü sonra kodu ile ilgili sözler blizpasta


@DrG: "break deyimi, göründüğü en yakın çevreleme döngüsünü veya anahtar deyimini sonlandırır." Şeklinde çalışmaz. (msdn)
blizpasta

1
@blizpasta So? Dış döngüdeki durumu yanlış yaparsa (yaptığı gibi), her ikisinden de çıkacaktır.
Patrick

51
i = INT_MAX - 1 kullanmalısınız; aksi halde i ++ == INT_MIN <100 ve döngü devam eder
Meta

6
foreach hakkında bir fikrin var mı?
ktutnik

4
@ktutnik Foreach ile çalışmaz, çünkü gizli numaralandırıcıya kod erişiminiz olmaz. Ayrıca IEnumerator bazı "MoveToEnd" yöntemi yok.
ygoe

39

Bu çözüm C # için geçerli değildir

Bu soruyu diğer diller aracılığıyla bulan kullanıcılar için Javascript, Java ve D etiketli molalara izin verir ve devam eder :

outer: while(fn1())
{
   while(fn2())
   {
     if(fn3()) continue outer;
     if(fn4()) break outer;
   }
}

40
bu c # ile yapılamaz üzücü, birçok kez daha temiz kod üretmek.
Rickard

17
Bu c # için DEĞİL olduğunu anlayana kadar aslında bir saniye için heyecanlandım. :(
Arvo Bowen

1
Bu kavram aynı zamanda birinin problemle karşılaşması durumunda PowerShell içindir . (İki nokta üst üste etiket adının önüne koyulur.) Şimdi bunun PowerShell'e özgü olmadığını biliyorum ... Bu sözdizimi C # 'da bulunan goto etiketleriyle uyumsuz olurdu. PHP başka bir şey kullanır: break 3; Sonlama sayısını break ifadesinden sonra koyun.
ygoe

1
Bu java değil c #
Luca Ziegler

Bu özelliği C # 'da görmek isteyenler için kudolarınızı buraya yerleştirin, lütfen 😉 github.com/dotnet/csharplang/issues/869#issuecomment-326606003
m93a

28

Dış halkada uygun bir koruyucu kullanın. Kırmadan önce koruyucuyu iç halka içine yerleştirin.

bool exitedInner = false;

for (int i = 0; i < N && !exitedInner; ++i) {

    .... some outer loop stuff

    for (int j = 0; j < M; ++j) {

        if (sometest) {
            exitedInner = true;
            break;
        }
    }
    if (!exitedInner) {
       ... more outer loop stuff
    }
}

Ya da daha iyisi, iç döngüyü bir yönteme soyutlayın ve false döndürdüğünde dış döngüden çıkın.

for (int i = 0; i < N; ++i) {

    .... some outer loop stuff

    if (!doInner(i, N, M)) {
       break;
    }

    ... more outer loop stuff
}

4
OP hariç "Ben bir boolean kullanmak istemiyorum" dedi.
LeopardSkinPillBoxHat

Çok Pascal-ish ... Muhtemelen bir goto kullanmayı tercih ederim, ancak normal olarak veba gibi onlardan kaçınırım.
Jonathan Leffler

21

Bu konuda bana alıntı yapma, ama MSDN'de önerildiği gibi goto kullanabilirsiniz . Her iki döngünün her yinelemesinde kontrol edilen bir bayrak dahil olmak üzere başka çözümler de vardır. Son olarak, bir istisnayı probleminize gerçekten ağır bir çözüm olarak kullanabilirsiniz.

GOTO:

for ( int i = 0; i < 10; ++i ) {
   for ( int j = 0; j < 10; ++j ) {
      // code
      if ( break_condition ) goto End;
      // more code
   }
}
End: ;

Durum:

bool exit = false;
for ( int i = 0; i < 10 && !exit; ++i ) {
   for ( int j = 0; j < 10 && !exit; ++j ) {
      // code
      if ( break_condition ) {
         exit = true;
         break; // or continue
      }
      // more code
   }
}

İstisna:

try {
    for ( int i = 0; i < 10 && !exit; ++i ) {
       for ( int j = 0; j < 10 && !exit; ++j ) {
          // code
          if ( break_condition ) {
             throw new Exception()
          }
          // more code
       }
    }
catch ( Exception e ) {}

2
bunların hepsi sadece bir yönteme katmak ve erken dönüşü kullanmak için süper temiz olacak hacky geçici çözümlerdir
Dustin Getz

3
:) doğru, bu basit bir çözüm, ancak gerekli tüm yerel verileri yönteme argüman olarak geçirmeniz gerekecek ... Bu,
goto'nun

Koşul döngüsü çalışmaz, çünkü iç döngüden çıktıktan sonra bir kez daha dış döngüden çıkmadan "daha fazla kod" yürütülür. GOTO yöntemi çalışır ancak posterin yapmak istemediklerini tam olarak söyler. İstisna yöntemi çalışır ancak GOTO'dan daha çirkin ve yavaştır.
Windows programcısı

Etiketten sonra noktalı virgülün altını çizeceğim. Bu şekilde bu etiket bir bloğun sonunda bile olabilir. +1
Tamas Hegedus

@Windowsprogrammer OP sordu, "Bu konuda hızlı ve güzel bir yol nedir?" Goto tercih edilen çözümdür: ayrı bir yöntem içermeden temiz ve özlü.
Suncat2000

16

Nested for loop'u özel bir yönteme dönüştürmek mümkün mü? Bu şekilde, döngüden çıkmak için yöntemden 'geri' dönebilirsiniz.


Orijinal yönteminizi kısaltmanın yan yararı ile :-)
Martin Capodici

C ++ 11 lambdas bunu bazı durumlar için kolaylaştırır:[&] { ... return; ... }();
BCS

14

Bana öyle geliyor ki insanlar bir gotoifadeden çok hoşlanmıyorlar , bu yüzden bunu biraz düzeltmem gerektiğini hissettim.

İnsanların gotoeninde sonunda sahip oldukları 'duyguların' kodun anlaşılmasıyla ve olası performans sonuçlarıyla ilgili (yanlış anlamalar) kaybolacağına inanıyorum . Bu soruyu cevaplamadan önce, nasıl derlendiğine dair bazı ayrıntılara gireceğim.

Hepimizin bildiği gibi, C # IL'ye derlenir ve daha sonra bir SSA derleyicisi kullanarak montajcıya derlenir. Tüm bunların nasıl çalıştığı hakkında biraz bilgi vereceğim ve ardından sorunun kendisine cevap vermeye çalışacağım.

C # 'dan IL' ye

İlk önce bir parça C # koduna ihtiyacımız var. Basit başlayalım:

foreach (var item in array)
{
    // ... 
    break;
    // ...
}

Kaputun altında neler olduğu hakkında iyi bir fikir vermek için bunu adım adım yapacağım.

İlk çeviri: den foreacheşdeğer fordöngüye (Not: Burada bir dizi kullanıyorum, çünkü IDisposable'ın ayrıntılarına girmek istemiyorum - bu durumda da bir IEnumerable kullanmak zorunda kalacağım):

for (int i=0; i<array.Length; ++i)
{
    var item = array[i];
    // ...
    break;
    // ...
}

İkinci çeviri: forve break, daha kolay bir karşılığıdır:

int i=0;
while (i < array.Length)
{
    var item = array[i];
    // ...
    break;
    // ...
    ++i;
}

Ve üçüncü çeviri (bu IL kod eşdeğerdir): Biz değiştirmek breakve whilebir şube haline:

    int i=0; // for initialization

startLoop:
    if (i >= array.Length) // for condition
    {
        goto exitLoop;
    }
    var item = array[i];
    // ...
    goto exitLoop; // break
    // ...
    ++i;           // for post-expression
    goto startLoop; 

Derleyici bunları tek bir adımda gerçekleştirirken, süreç hakkında size fikir verir. C # programından gelişen IL kodu , son C # kodunun gerçek çevirisidir . Burada kendiniz görebilirsiniz: https://dotnetfiddle.net/QaiLRz ('IL'yi görüntüle' bağlantısını tıklayın)

Şimdi, burada gözlemlediğiniz bir şey, işlem sırasında kodun daha karmaşık hale gelmesidir. Bunu gözlemlemenin en kolay yolu, aynı şeyi kabul etmek için gittikçe daha fazla koda ihtiyaç duymamızdır. Ayrıca iddia edebilir foreach, for, whileve breakaslında kısa ellerdir gotokısmen doğru olan.

IL'den Assembler'a

.NET JIT derleyicisi bir SSA derleyicisidir. Burada SSA formunun tüm ayrıntılarına ve optimize edici bir derleyicinin nasıl oluşturulacağına girmeyeceğim, çok fazla, ancak ne olacağı hakkında temel bir anlayış verebilir. Daha derin bir anlayış için, derleyicileri optimize etmeye okumaya başlamak en iyisidir (kısa bir giriş için bu kitabı beğendim: http://ssabook.gforge.inria.fr/latest/book.pdf ) ve LLVM (llvm.org) .

Her optimize edici derleyici, kodun kolay olduğu ve öngörülebilir kalıpları takip ettiği gerçeğine dayanır . FOR döngüleri durumunda, dalları analiz etmek için grafik teorisini kullanırız ve sonra da dallarımızdaki cycli gibi şeyleri optimize ederiz (örn. Geriye doğru dallar).

Ancak, şimdi döngülerimizi uygulamak için ileri şubelerimiz var. Tahmin edebileceğiniz gibi, bu aslında JIT'in düzelteceği ilk adımlardan biridir, şöyle:

    int i=0; // for initialization

    if (i >= array.Length) // for condition
    {
        goto endOfLoop;
    }

startLoop:
    var item = array[i];
    // ...
    goto endOfLoop; // break
    // ...
    ++i;           // for post-expression

    if (i >= array.Length) // for condition
    {
        goto startLoop;
    }

endOfLoop:
    // ...

Gördüğünüz gibi, artık geri döngümüz var, bu da bizim küçük döngümüz. Burada hala kötü olan tek şey, breakaçıklamamız nedeniyle sonuçta bulunduğumuz şubedir. Bazı durumlarda, bunu aynı şekilde hareket ettirebiliriz, ancak bazılarında kalmak için oradadır.

Peki derleyici bunu neden yapıyor? Döngüyü çözebilirsek, vektörleştirebiliriz. Sadece sabitlerin eklendiğini bile kanıtlayabiliriz, yani tüm döngümüz ince havaya kaybolabilir. Özetlemek gerekirse: paternleri öngörülebilir hale getirerek (dalları öngörülebilir hale getirerek), döngümüzde belirli koşulların geçerli olduğunu kanıtlayabiliriz, yani JIT optimizasyonu sırasında sihir yapabiliriz.

Bununla birlikte, dallar bu güzel öngörülebilir kalıpları kırma eğilimindedir, bu da optimize edicilerdir ve bu nedenle hoşnutsuz bir şeydir. Kır, devam et, git - hepsi bu öngörülebilir kalıpları kırmayı planlıyor - ve bu yüzden gerçekten 'hoş' değiller.

Bu noktada, bir basitliğin her yere yayılan foreachbir grup gotoifadeden daha öngörülebilir olduğunu da fark etmelisiniz . Optimize edici bakış açısından (1) okunabilirlik ve (2) açısından, hem daha iyi çözümdür.

Bahsetmeye değer başka bir şey de, derleyicileri değişkenlere kayıt atamak için optimize etmenin çok alakalı olmasıdır ( kayıt tahsisi adı verilen bir süreç ). Bildiğiniz gibi, CPU'nuzda sadece sınırlı sayıda kayıt vardır ve bunlar donanımınızdaki en hızlı bellek parçalarıdır. En içteki döngüde kodda kullanılan değişkenlerin bir kayıt atanması daha olasıdır, ancak döngünüzün dışındaki değişkenler daha az önemlidir (çünkü bu kod muhtemelen daha az isabetlidir).

Yardım, çok fazla karmaşıklık ... ne yapmalıyım?

Sonuç olarak, her zaman emrinizde olan dil yapılarını kullanmalısınız, bu da derleyiciniz için genellikle (dolaylı olarak) tahmin edilebilir kalıplar oluşturur. (: Özellikle mümkünse garip dalları kaçının break, continue, gotoya da returnhiçbir şey ortasında).

Buradaki iyi haber şu ki bu öngörülebilir kalıplar hem insanlar için hem de okunması kolay (derleyiciler için).

Bu kalıplardan birine SESE denir ve Tek Girişli Tek Çıkış anlamına gelir.

Ve şimdi gerçek soruya geçiyoruz.

Bunun gibi bir şeyiniz olduğunu düşünün:

// a is a variable.

for (int i=0; i<100; ++i) 
{
  for (int j=0; j<100; ++j)
  {
     // ...

     if (i*j > a) 
     {
        // break everything
     }
  }
}

Bunu öngörülebilir bir desen haline getirmenin en kolay yolu, iftamamen ortadan kaldırmaktır :

int i, j;
for (i=0; i<100 && i*j <= a; ++i) 
{
  for (j=0; j<100 && i*j <= a; ++j)
  {
     // ...
  }
}

Diğer durumlarda yöntemi de 2 yönteme bölebilirsiniz:

// Outer loop in method 1:

for (i=0; i<100 && processInner(i); ++i) 
{
}

private bool processInner(int i)
{
  int j;
  for (j=0; j<100 && i*j <= a; ++j)
  {
     // ...
  }
  return i*j<=a;
}

Geçici değişkenler? İyi, kötü ya da çirkin mi?

Hatta döngü içinden bir boolean döndürmeye karar verebilirsiniz (ama ben şahsen SESE formunu tercih ederim çünkü derleyici bunu görecek ve okumak daha temiz olduğunu düşünüyorum).

Bazı insanlar geçici bir değişken kullanmanın daha temiz olduğunu düşünüyor ve böyle bir çözüm öneriyor:

bool more = true;
for (int i=0; i<100; ++i) 
{
  for (int j=0; j<100; ++j) 
  {
     // ...
     if (i*j > a) { more = false; break; } // yuck.
     // ...
  }
  if (!more) { break; } // yuck.
  // ...
}
// ...

Ben şahsen bu yaklaşıma karşıyım. Kodun nasıl derlendiğine tekrar bakın. Şimdi bunun bu güzel, öngörülebilir kalıplarla ne yapacağını düşünün. Resmi al?

Doğru, heceleyelim. Ne olacak:

  • Derleyici her şeyi dal olarak yazacaktır.
  • Bir optimizasyon adımı olarak, derleyici moresadece kontrol akışında kullanılan garip değişkeni ortadan kaldırmak için veri akışı analizi yapacaktır .
  • Başarılı olursa, değişken moreprogramdan kaldırılır ve yalnızca dallar kalır. Bu dallar optimize edilecek, böylece iç döngüden sadece tek bir dal alacaksınız.
  • Eğer başarısız olursa, değişken moreen içteki döngüde kesinlikle kullanılır, bu nedenle derleyici onu optimize etmezse, bir kayda tahsis etme şansı yüksektir (değerli kayıt hafızasını tüketir).

Özetlemek gerekirse: derleyicinizdeki optimizer more, yalnızca kontrol akışı için kullanıldığını anlamak için çok fazla sorun yaşayacaktır ve en iyi durumda senaryo , onu dış için tek bir dala çevirecektir. döngü.

Başka bir deyişle, en iyi senaryo, bunun eşdeğeri ile sonuçlanacağı yönündedir:

for (int i=0; i<100; ++i) 
{
  for (int j=0; j<100; ++j)
  {
     // ...
     if (i*j > a) { goto exitLoop; } // perhaps add a comment
     // ...
  }
  // ...
}
exitLoop:

// ...

Bununla ilgili kişisel düşüncem oldukça basit: eğer başından beri istediğimiz buysa, dünyayı hem derleyici hem de okunabilirlik için kolaylaştıralım ve hemen yazalım.

tl; dr:

Sonuç olarak:

  • Mümkünse for döngüsü içinde basit bir koşul kullanın. Mümkün olduğunca üst düzey dil yapılarına bağlı kalın.
  • Her şey başarısız olursa ve ya sol ediyorsanız gotoveya bool moreeski tercih ederler.

OTOH: Nadiren dış döngüleri kırma veya goto-equiv (ve tartışmasız daha temiz yazılabilecek bir sürü kod) arzusu var, ancak bu diğer alanlarda daha yaygın olabilir, ancak bugün böyle bir durumdu, ama bunun nedeni büyük ölçüde yield return. Yani, bazı yararlı durumlar olduğunu göstermek kolay olsa da, çoğu "uygulama düzeyinde" kod için muhtemelen C # ile gelen çok düşük bir hayal kırıklığı oluşumu, "sadece gelen" C / C ++ ;-) hariç, ben de bazen özlüyorum Ruby'nin bu alanda da yer aldığı için "fırlatma" (İstisnai olmayan gevşetme) vesilesiyle.
user2864740

1
@ Suncat2000 Sadece kullanmaktan kaçınmak için değişkenler ve koşullu dallar gibi daha fazla kod tanıtmak goto, kodunuzu daha okunabilir hale getirmez - tam olarak bunun tam tersi olduğunu iddia ederim: aslında kontrol akışınız daha fazla düğüm içerecektir - ki bu oldukça fazla karmaşıklığın tanımı. Sadece kendini disipline yardımcı olmaktan kaçınmak, okunabilirlik açısından üretkendir.
atlaste

@atlaste: Üzgünüm, yorumumu daha net ifade edeyim. Söylemeliydim: Güzel bir açıklama. Ancak gitmekten kaçınan insanlar performansla ilgili değildir; okunabilirlik eksikliğine neden olan kötüye kullanımdan kaçınmakla ilgilidir. Bundan kaçınan insanların goto, onu kötüye kullanmama disiplini olmadığını varsayabiliriz .
Suncat2000

@atlaste, "ackomplish" nedir?
Pacerier

11

bir işleve / yönteme dönüştürün ve erken dönüş kullanın veya döngülerinizi bir süre yan tümcesinde yeniden düzenleyin. goto / istisnalar / burada kesinlikle uygun olmayan ne varsa.

def do_until_equal():
  foreach a:
    foreach b:
      if a==b: return

10

Hızlı, güzel, boole kullanmama, goto kullanmama ve C # kombinasyonunu istediniz. İstediğinizi yapmanın tüm olası yollarını dışladınız.

En hızlı ve en az çirkin yol bir goto kullanmaktır.


Tamamen katılıyorum. Sadece tek bir goto'dan kurtulmak için yeni bir yöntem getirmek aptalca. Derleyici bu yöntemi çağırmak için herhangi bir sebeple satır içi yapamazsa - ek bir yöntem çağrısının tamamen gereksiz yükü ile sonuçlanır. Sadece döngüyü kırmak için istisnaları atmak ve yakalamak hem daha ayrıntılı hem de gülünç derecede daha pahalıdır.
Zar Shardan

@Windowsprogramm: OP "goto kullanımı yok" diye sormadı. "Başka bir yönteme gitmek" istemedi. Soru, tüm olası yolları dışlamaktan uzaktı , ancak goto'nun burada en iyisi olduğunu ima etmekte haklısınız.
Suncat2000

5

Bazen kodu kendi işlevine soyutlamak güzel ve erken bir dönüş kullanmaktan daha erken - erken dönüşler kötüdür:)

public void GetIndexOf(Transform transform, out int outX, out int outY)
{
    outX = -1;
    outY = -1;

    for (int x = 0; x < Columns.Length; x++)
    {
        var column = Columns[x];

        for (int y = 0; y < column.Transforms.Length; y++)
        {
            if(column.Transforms[y] == transform)
            {
                outX = x;
                outY = y;

                return;
            }
        }
    }
}

1
ancak bu OP tarafından sorulan geleneksel çözüme izin verir
Surya Pratap

2

Ben "break" kullanan bir çok örnek gördüm ama "devam" kullanan hiçbiri.

İç döngüde hala bir tür bayrak gerekir:

while( some_condition )
{
    // outer loop stuff
    ...

    bool get_out = false;
    for(...)
    {
        // inner loop stuff
        ...

        get_out = true;
        break;
    }

    if( get_out )
    {
        some_condition=false;
        continue;
    }

    // more out loop stuff
    ...

}

1

breakC'de birkaç on yıl önce ilk gördüğümden beri , bu sorun beni sinirlendirdi. Bazı dil geliştirme, böylece işe yarayacak bir uzantısı olacak umuyordum:

break; // our trusty friend, breaks out of current looping construct.
break 2; // breaks out of the current and it's parent looping construct.
break 3; // breaks out of 3 looping constructs.
break all; // totally decimates any looping constructs in force.

3
Daha sonra bir bakım programcısı başka bir iç içe yerleştirme düzeyi ekler, kesme ifadelerinin bazılarını düzeltir ve diğer kesme ifadelerinin bazılarını keser. Bunun çözümü, bir etikete kırmaktır. Bu gerçekten önerildi, ancak pragmatistler bunun yerine bir etiket kullanıyorlar.
Windows programcısı

Bekle, bakım programlaması artık kim? :)
Jesse C. Slicer

2
JavaScript bile blok / kesme ifadelerini etiketlemiştir. devguru.com/Technologies/ecmascript/quickref/break.html
David Grant

@Chris Bartow: harika! Noelimi yaptı :) @David Grant: Öyleyse JS sonu == C'nin gitti mi?
Jesse C. Slicer

D mola / devam ediyor
BCS

0

Öğrenci günlerimden, bir goto olmadan kodda herhangi bir şey yapabileceğinizin matematiksel olarak kanıtlanabilir olduğunu hatırlıyorum (yani, goto'nun tek cevap olduğu bir durum yok). Yani, asla goto'ları kullanmam (sadece kişisel tercihim, doğru ya da yanlış olduğumu düşündürmüyorum)

Her neyse, iç içe döngülerden kurtulmak için şöyle bir şey yapıyorum:

var isDone = false;
for (var x in collectionX) {
    for (var y in collectionY) {
        for (var z in collectionZ) {
            if (conditionMet) {
                // some code
                isDone = true;
            }
            if (isDone)
                break;
        }
        if (isDone) 
            break;
    }
    if (isDone)
        break;
}

... umarım bu benim gibi olanlar için anti-goto "fanboylar" yardımcı olur :)


Sana söylediğim için üzgünüm, ama senin okutmanın senin durumundan sorumlu olacak. Sizi montaj öğrenmeye zorlamak için uğraşmışsa, o zaman 'goto'nun sadece bir sıçrama olduğunu biliyorsunuz (elbette bunun ac # soru olduğu gerçeğini görmezden geliyorum).
cmroanirgo

0

Ben böyle yaptım. Hala bir çözüm.

foreach (var substring in substrings) {
  //To be used to break from 1st loop.
  int breaker=1;
  foreach (char c in substring) {
    if (char.IsLetter(c)) {
      Console.WriteLine(line.IndexOf(c));
      \\setting condition to break from 1st loop.
      breaker=9;
      break;
    }
  }
  if (breaker==9) {
    break;
  }
}


0

Çift döngüyü sonlandırmanın en kolay yolu doğrudan ilk döngüyü bitirmek olacaktır

string TestStr = "The frog jumped over the hill";
char[] KillChar = {'w', 'l'};

for(int i = 0; i < TestStr.Length; i++)
{
    for(int E = 0; E < KillChar.Length; E++)
    {
        if(KillChar[E] == TestStr[i])
        {
            i = TestStr.Length; //Ends First Loop
            break; //Ends Second Loop
        }
    }
}

Bu, mola kullanarak iç döngüyü sonlandıracağınız için çalışmaz. Dış döngü yinelemeye devam edecektir.
Alex Leo

0

Döngüler, döngüdeki özel koşullar kullanılarak kırılabilir, böylece temiz kod elde edilir.

    static void Main(string[] args)
    {
        bool isBreak = false;
        for (int i = 0; ConditionLoop(isBreak, i, 500); i++)
        {
            Console.WriteLine($"External loop iteration {i}");
            for (int j = 0; ConditionLoop(isBreak, j, 500); j++)
            {
                Console.WriteLine($"Inner loop iteration {j}");

                // This code is only to produce the break.
                if (j > 3)
                {
                    isBreak = true;
                }                  
            }

            Console.WriteLine("The code after the inner loop will be executed when breaks");
        }

        Console.ReadKey();
    }

    private static bool ConditionLoop(bool isBreak, int i, int maxIterations) => i < maxIterations && !isBreak;   

Bu kod ile aşağıdaki çıktıyı elde ederiz:

  • Harici döngü yineleme 0
  • İç döngü yinelemesi 0
  • İç döngü yinelemesi 1
  • İç döngü yinelemesi 2
  • İç döngü yinelemesi 3
  • İç döngü yinelemesi 4
  • İç döngüden sonraki kod, kesildiğinde yürütülür

0

Diğer bir seçenek, kendi kendine çağrılan anonim bir işlevdir . Git yok, etiket yok, yeni değişken yok, yeni işlev adı yok ve üstteki anonim yöntem örneğinden bir satır daha kısa .

// self-invoked-anonymous-function
new Action(() =>
{
    for (int x = 0; x < 100; x++)
    {
        for (int y = 0; y < 100; y++)
        {
            return; // exits self invoked lambda expression
        }
    }
})();
Console.WriteLine("Hi");

-3

Çıkış döngüsü dışına çıkan özel bir istisna atın.

Şunun için çalışır for, foreachya da whiledöngü her türlü ve herhangi bir dil olduğunu veya kullandığı try catch exceptionblok

try 
{
   foreach (object o in list)
   {
      foreach (object another in otherList)
      {
         // ... some stuff here
         if (condition)
         {
            throw new CustomExcpetion();
         }
      }
   }
}
catch (CustomException)
{
   // log 
}

-4
         bool breakInnerLoop=false
        for(int i=0;i<=10;i++)
        {
          for(int J=0;i<=10;i++)
          {
              if(i<=j)
                {
                    breakInnerLoop=true;
                    break;
                }
          }
            if(breakInnerLoop)
            {
            continue
            }
        }

Dviljoen'in cevabı ile arasındaki temel fark nedir?
Gert Arnold

Bu, çalışmaz, çünkü döngü için dıştaki "breakInnerLoop" koşulunu kontrol etmezsiniz, bu yüzden sadece bir sonraki döngüye tekrarlarsınız, ayrıca j ve J yazdınız ve derlemeyecek bir noktalı virgül kaçırdınız.
Sebastian

-4

Gördüğüm gibi, kişinin sizi ifade ettiği ifadeyi kabul ettiğinizi, modern programlamada ve uzman görüşünde goto'nun bir katil olduğu, bunu programlamada bazı nedenleri olan bir katil olarak adlandırdık, burada tartışmayacağım bu noktada, ancak sorunuzun çözümü çok basit, bu tür bir senaryoda bir Boole bayrağı kullanabilirsiniz, örneğin örneğimde göstereceğim gibi:

            for (; j < 10; j++)
            {
                //solution
                bool breakme = false;
                for (int k = 1; k < 10; k++)
                {
                   //place the condition where you want to stop it
                    if ()
                    {
                        breakme = true;
                        break;
                    }
                }

                if(breakme)
                    break;
               }

basit ve sade. :)


mola vermeden önce soruyu okuyun. Bu yüzden aşağı oylar.
cmroanirgo

1
şimdi okuyun, ama ben bu cevabı gönderdi boolean kullanarak değil düzenlenmiş sürümü eklenmedi bu yüzden ben bu cevabı gönderdi .. ama downvote için teşekkürler!
Steve

1
Kişisel bir şey değil. Ne yazık ki, oy artık kilitli;) (Her zaman gerçekleşmeyen) en iyi cevapları almak için SO'nun gerekli bir parçası
cmroanirgo

-6

Hatta baktın mı breakanahtar kelime? Oo

Bu sadece sözde kod, ama ne demek istediğimi görebilmelisiniz:

<?php
for(...) {
    while(...) {
        foreach(...) {
            break 3;
        }
    }
}

breakGibi bir işlev olmayı düşünüyorsanızbreak() , parametre değiştirilecek döngü sayısı olacaktır. Buradaki kodun üçüncü döngüsünde olduğumuz için, üçünden de ayrılabiliriz.

Manuel: http://php.net/break


13
Bir php sorusu değil.
Zerga

-10

Bence "boolean şeyi" yapmak istemiyorsanız tek çözüm aslında atmaktır. Açıkçası yapmamalısın ..!

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.