İç içe geçmiş döngülerden nasıl çıkılır?


98

Bir breakifade kullanırsam , yalnızca iç döngüyü kırar ve dış döngüyü kırmak için biraz bayrak kullanmam gerekir. Ancak çok sayıda iç içe geçmiş döngü varsa, kod iyi görünmeyecektir.

Tüm döngüleri kırmanın başka bir yolu var mı? (Lütfen kullanmayın goto stmt.)

for(int i = 0; i < 1000; i++) {
   for(int j = 0; j < 1000; j++) {
       if(condition) {
            // both of the loops need to break and control will go to stmt2
       }
   }

}

stmt2

2
döngü başlamadan önce int i ve int j'yi deneyebilirsiniz ve sonra onları 1001 yapmak koşulunda döngü bir sonrakini yinelemeyecektir.
Khurram Ijaz

Yanıtlar:


45

Kullanım:

if (condition) {
    i = j = 1000;
    break;
}

50
İşe yarıyor, ama çirkin ve genel değil. Ya birisi sınırı 2000 olarak değiştirirse (kodun daha uzun olduğunu ve böylece hemen fark etmeyeceğinizi varsayalım)?
ugoren

1
@ugoren O zaman da çok basit. ya const int count =1000 genel Başlatma'da a kullandıysanız. veya #definemakro olarak.
Laksith

4
@Ugoren'in belirttiği gibi, bu genel bir çözüm değil. Bu, bu sorunun ilk Google isabeti olduğundan, genel çözüm seçilmiş olsaydı iyi olurdu. İnsanlar yine de # 2'yi kontrol etmeye alışkındır.
BeeOnRope

1
Sanırım sadece i = 1000'e ihtiyacım var?
Peter Wu

191

Hayır, eğlenceyi bir break. Bu kalan son geçerli kullanımdır goto;)

Bu değilse, derin iç içe döngülerden çıkmak için bayrakları kullanabilirsiniz.

Yuvalanmış bir döngüden çıkmanın başka bir yolu, her iki döngüyü de ayrı bir işleve ayırmak ve çıkmak istediğinizde bu işlevden geri dönmektir.

Özetlenmiş - iç içe döngülerden çıkmak için:

  1. kullanım goto
  2. bayrakları kullan
  3. döngüleri ayrı işlev çağrılarına ayırın

Burada xkcd dahil olmaktan direnemedim :)

görüntü açıklamasını buraya girin

kaynak

Goto'lar zararlı olarak kabul edilir, ancak yorumlarda yer alan birçok kişinin söylediği gibi olması gerekmez. Mantıklı bir şekilde kullanılırsa harika bir araç olabilir. Ölçülü olarak kullanılan her şey eğlencelidir.


30
Goto buraya geleceğiniz kadar net, evet. Çıkış değişkenini 1000 olarak ayarlamak daha da karmaşıktır.
correnos

3
Gotos'un açıkça kötü olmadığını, sadece kötülük için kullanılabileceğini eklemek isterim. Oldukça az sayıda durum olduğunu görüyorum, örneğin bu, en iyi çözümdür. "Gotos kullanma" iyi bir başlangıçtır, ancak beceride bir sonraki adımın "uzun menzilli gotos kullanma" yapmanıza olanak sağladığını düşünüyorum.
04:37

1
Buna katılmıyorum: "Bir işlev oluşturmak, üstel miktarlarda yığın işaretçisinin toplanması / çıkarılmasıyla sonuçlanır". Program akışında yalnızca bir noktada çağrılan yerel (statik) bir işlev varsa, yarı düzgün bir derleyici onu satır içi yapar ve sonuçta ortaya çıkan kod esasen goto ile aynı olur. Bu, herhangi bir derleyici için muhtemelen en kolay optimizasyon durumu.
DrV

1
Yeniden düzenleme genellikle en temiz çözümdür. Bununla birlikte, iç döngü sırasında herhangi bir döngü dışı değişken değiştirilirse, işler karmaşıklaşır. Bir olasılık, değişkeni referans (işaretçi) ile iç işleve geçirmektir, ancak bu, derleyici optimizasyonunu karıştırabilir ve gereksiz fazladan kod üretebilir. Diğer bir olasılık, bu tür değişkenleri modül düzeyinde statik yapmaktır, ancak bu da çok güzel değildir. C maalesef bu sorunu çözecekleri için iç içe geçmiş işlevler eksik - kendinizi bir uzantı sağlayan gcc kullanmaya istekli değilseniz.
DrV

2
+1. Donald E. Knuth'un go Statements ( wiki.c2.com/?StructuredProgrammingWithGoToStatements ) ile Yapılandırılmış Programlaması Dijkstra'ları dengelemek için ilginç bir makale.
kmkaplan

41
bool stop = false;
for (int i = 0; (i < 1000) && !stop; i++)
{
    for (int j = 0; (j < 1000) && !stop; j++)
    {
        if (condition)
            stop = true;
    }
}

Çözüm, kesintide her iki değişkeni de birer birer
artırarak soruna

8
"Stop = true;" ve sonra "ara;". Sonra, "for" döngüsünün iç kısmının bitiminden hemen sonra, "if (stop) break;" yapın.
Jeff Grigg

Bence bu en zarif gotoçözüm olmayan şey . Koşulların sınırlarını ive jiçinde olmayı bilmeyi gerektirmez . Ayarlamadan sonra if (stop) break;kendi kendine olabilecek iç döngüden sonra koyma fikrini seviyorum . breakstop
Willis Blackburn

35

Bunun bir yolu, tüm iç içe döngüleri bir işleve koymak ve tüm döngülerden kopma ihtiyacı durumunda en içteki döngüden geri dönmek.

function() 
{    
  for(int i=0; i<1000; i++)
  {
   for(int j=0; j<1000;j++)
   {
      if (condition)
        return;
   }
  }    
}

2
benim için en iyi çözüm gibi görünüyor
Luca Steeb

21

Bence gotosorunu çözecek

for(int i = 0; i < 1000; i++) {
    for(int j = 0; j < 1000; j++) {
        if (condition) {
            goto end;
        }
    }
}

end:
stmt2 

 

@chikuba ben den cevap var cprogramming.com/tutorial/goto.html ve ben senin yayını görebilen daha dont neden ben aynı şey Thats yapıyorum zaman cevap yayınlanmıştır değil
renjith KN

16

Okunabilmesini istiyorsanız, bir boole değişkenine ihtiyacınız olacak:

bool broke = false;
for(int i = 0; i < 1000; i++) {
  for(int j = 0; j < 1000; i++) {
    if (condition) {
      broke = true;
      break;
    }
  }
  if (broke)
    break;
}

Daha az okunabilir olmasını istiyorsanız, boole değerlendirmesine katılabilirsiniz:

bool broke = false;
for(int i = 0; i < 1000 && !broke; i++) {
  for(int j = 0; j < 1000; i++) {
    if (condition) {
      broke = true;
      break;
    }
  }
}

Nihai bir yol olarak, ilk döngüyü geçersiz kılabilirsiniz:

for(int i = 0; i < size; i++) {
  for(int j = 0; j < 1000; i++) {
    if (condition) {
      i = size;
      break;
    }
  }
}


4

Dikkat: Bu cevap gerçekten belirsiz bir yapıyı göstermektedir.

GCC kullanıyorsanız, bu kitaplığa göz atın . PHP'de olduğu gibi, breakçıkmak istediğiniz iç içe döngülerin sayısını kabul edebilir. Bunun gibi bir şey yazabilirsiniz:

for(int i = 0; i < 1000; i++) {
   for(int j = 0; j < 1000; j++) {
       if(condition) {
            // break two nested enclosing loops
            break(2);
       }
   }
}

Ve kaputun altında gerçekten kullanıyorgoto :)
jacobq

@ iX3 Eğer yardımcı olursa satır içi assembler ve jmp komutunu kullanabilirim.
DaBler

@DaBler, o kütüphanenin yazarı olduğunu bilmiyordum. Yorumum geri bildirim olarak değil, bu kütüphanenin kabul edilen cevapla aynı yöntemi kullandığına dikkat çekiyordu . Umarım yorumunuz bir şaka olarak ifade edilmiştir, çünkü bir dil özelliğini (hatta goto) kullanmanın inline asm (makineye özgü, hata yapması daha kolay, okunması daha zor, ...) için çok daha uygun olduğunu düşünüyorum.
jacobq

3
for(int i = 0; i < 1000; i++) {
   for(int j = 0; j < 1000; i++) {
       if(condition) {
            goto end;
   }
} 

end:

3

İ ve j değerlerine ihtiyacınız varsa, bu işe yaramalı, ancak diğerlerinden daha az performansla

for(i;i< 1000; i++){    
    for(j; j< 1000; j++){
        if(condition)
            break;
    }
    if(condition) //the same condition
        break;
}

Koşul bağlıysa, jbunun hala çalışması için koşulun değerinin bir şekilde saklanması gerektiğini unutmayın.
SuperBiasedMan

1
Haklısınız ama moladan sonra j'nin değeri değişmez ve koşulun değeri de değişir.
Ali Eren Çelik

Bu bozuk bir çözümdür ve genel olarak geçerli değildir. Ya j onun döngü dışında tanımlı değil ya for (int i = 0; i < 1000; i++) { for (int j = 0; j < 1000; j++) { if (workComplete[i][j]) break; /* do work */ workComplete[i][j] = true; } if (workComplete[i][j]) break; ... }gidiyor hep iç döngünün ilk yineleme sonra dış döngünün patlak.
Chai T. Rex

0

Farklı bir yaklaşım, kodu iki for döngüsünden bir for döngüsüne ve bir manuel döngüye yeniden düzenlemektir. Bu şekilde manuel döngüdeki kesinti dış döngü için geçerlidir. Bunu , işlemek için üç iç içe döngü gerektiren bir Gauss-Jordan Eliminasyonunda bir kez kullandım .

for (int i = 0; i < 1000; i++)
{
    int j = 0;

MANUAL_LOOP:;

    if (j < 1000)
    {
       if (condition)
       {
           break;
       }

       j++;
       goto MANUAL_LOOP;
    }
}

Eğer yapacaksanız goto, neden gotodöngünün dışında olmasın ?
Willis Blackburn

0

Sorunun basitçe "Tüm döngüleri kırmanın başka bir yolu var mı?" Herhangi bir yeterlilik görmüyorum ama öyle değil goto, özellikle OP iyi bir yol istemedi . Peki, longjmpiç döngüden çıksak nasıl olur? :-)

#include <stdio.h>
#include <setjmp.h>

int main(int argc, char* argv[]) {
  int counter = 0;
  jmp_buf look_ma_no_goto;
  if (!setjmp(look_ma_no_goto)) {
    for (int i = 0; i < 1000; i++) {
      for (int j = 0; j < 1000; j++) {
        if (i == 500 && j == 500) {
          longjmp(look_ma_no_goto, 1);
        }
        counter++;
      }
    }
  }
  printf("counter=%d\n", counter);
}

setjmpİşlev iki kez döner. İlk kez 0 döndürür ve program iç içe yerleştirilmiş döngüleri çalıştırır. Daha sonra her ikisi ive j500 olduğunda, çalıştırılır longjmpve bu setjmpda döngüden atlayarak 1 değeriyle tekrar dönmesine neden olur .

longjmpSizi yalnızca iç içe geçmiş döngülerin dışına çıkarmakla kalmaz , aynı zamanda iç içe geçmiş işlevlerle de çalışır!


-3
int i = 0, j= 0;

for(i;i< 1000; i++){    
    for(j; j< 1000; j++){
        if(condition){
            i = j = 1001;
            break;
        }
    }
}

Her iki döngüyü de kıracak.


-3
for(int i = 0; i < 1000; i++) {
    for(int j = 0; j < 1000; i++) {
       if(condition) {
          func(para1, para2...);
          return;
       }
    }
}

func(para1, para2...) {
    stmt2;
}

Yani temelde, (1) bir sürü ekstra işlev çağrısı yapması ve ardından (2) conditionyanlış olduğunda geri kalan süre boyunca dönmesi gerektiğini söylüyorsunuz . Oh, ve ikinci döngü sonsuza kadar çalışacak çünkü bunun iyerine j
artacak

-4
i = 0;

do
{
  for (int j = 0; j < 1000; j++) // by the way, your code uses i++ here!
  {
     if (condition)
     {
       break;
     }
  }

  ++i;

} while ((i < 1000) && !condition);
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.