Python: Dış döngüde sonraki yinelemeye devam ediliyor


136

Python'da dış döngüde bir sonraki yinelemeye devam etmenin herhangi bir yerleşik yolu olup olmadığını bilmek istedim. Örneğin, kodu düşünün:

for ii in range(200):
    for jj in range(200, 400):
        ...block0...
        if something:
            continue
    ...block1...

Bu continue ifadesinin jj döngüsünden çıkmasını ve ii döngüsündeki sonraki öğeye gitmesini istiyorum. Bu mantığı başka bir şekilde uygulayabilirim (bir bayrak değişkeni ayarlayarak), ancak bunu yapmanın kolay bir yolu var mı, yoksa bu çok fazlasını istemek gibi bir şey mi?


11
Aslında Python için çalışan bir goto ifadesi var: entrian.com/goto . Bir Nisan şakası olarak yayınlandı :-), ama işe yaraması gerekiyordu.
kodak

3
Lütfen o şakayı kullanma! Oldukça zekice, ancak daha sonra kodunuza eklerseniz üzüleceksiniz.
Ned Batchelder

Yanıtlar:


71
for i in ...:
    for j in ...:
        for k in ...:
            if something:
                # continue loop i

Genel bir durumda, birden fazla döngü düzeyiniz olduğunda ve breaksizin için çalışmadığında (çünkü mevcut döngüden değil, üst döngülerden birine devam etmek istiyorsanız), aşağıdakilerden birini yapabilirsiniz.

Kaçmak istediğiniz döngüleri bir işleve yeniden düzenleyin

def inner():
    for j in ...:
        for k in ...:
            if something:
                return


for i in ...:
    inner()

Dezavantajı, bu yeni işleve daha önce kapsamda olan bazı değişkenleri aktarmanız gerekebilmesidir. Bunları sadece parametre olarak iletebilir, onları bir nesne üzerinde örnek değişkenler yapabilirsiniz (eğer mantıklıysa, sadece bu fonksiyon için yeni bir nesne yaratabilirsiniz) veya global değişkenler, tekli tonlar, ne olursa olsun (ehm, ehm).

Veya inneriç içe geçmiş bir işlev olarak tanımlayabilir ve sadece ihtiyaç duyduğu şeyi yakalamasına izin verebilirsiniz (daha yavaş olabilir mi?)

for i in ...:
    def inner():
        for j in ...:
            for k in ...:
                if something:
                    return
    inner()

İstisnaları kullanın

Felsefi olarak, gerektiğinde yapısal programlama yapı taşlarından (if, for, while) program akışını kırmak için istisnalar budur.

Bunun avantajı, tek bir kod parçasını birden çok parçaya bölmeniz gerekmemesidir. Bu, Python'da yazarken tasarladığınız bir tür hesaplamaysa iyidir. Bu erken noktada soyutlamalara başlamak sizi yavaşlatabilir.

Bu yaklaşımla ilgili kötü olan şey, yorumlayıcı / derleyici yazarların genellikle istisnaların istisnai olduğunu varsaymaları ve buna göre optimize etmeleridir.

class ContinueI(Exception):
    pass


continue_i = ContinueI()

for i in ...:
    try:
        for j in ...:
            for k in ...:
                if something:
                    raise continue_i
    except ContinueI:
        continue

Bunun için özel bir istisna sınıfı oluşturun, böylece yanlışlıkla başka bir istisnayı susturma riskine girmezsiniz.

Tamamen başka bir şey

Eminim başka çözümler de vardır.


İkinci döngümü başka bir yönteme taşımayı düşünmediğime inanamıyorum. Ben yavaş alıyorum
pmccallum

1
Bana göre, İstisnaları kullanmak bunu başarmanın iyi bir yoludur. @ User7610'a katılıyorum - "felsefi olarak, istisnalar bunun içindir".
Renato Byrro

Güzel eski boole değişkeni ve If ifadeleri?
MrR

OP buna alternatif çözümler arıyor, "Bu mantığı başka bir şekilde uygulayabilirim (bir bayrak değişkeni ayarlayarak) [...]"
user7610

149
for ii in range(200):
    for jj in range(200, 400):
        ...block0...
        if something:
            break
    else:
        ...block1...

Break iç döngüyü kırar ve blok1 çalıştırılmaz (yalnızca iç döngüden normal olarak çıkılırsa çalışır).


1
Merhaba, bunun gibi başka seçenekler var mı? Çünkü blok1'de başka bir for döngüsü yapmak istiyorum ve bu şekilde kodum 3 seviye derine iner. Garip durum.
Sahas

3
Bana göre bu, farklı bir şekilde en iyi şekilde yaklaşılabilecek döngüler için bir şeyler yapmaya çalışıyormuşsunuz gibi geliyor ...
Kimvais

Evet. Bu yüzden for..else yapısını kullanmadım. Şimdi hala döngülere ihtiyacım var, ancak kontrolü yönlendirmek için bayrak değişkenlerini kullanacağım.
Sahas

3
for...elsekafa karıştırıcı olsa da, genellikle yararlı bir yapıdır. Bunun elsebu bağlamda "ara yok" anlamına geldiğini unutmayın .
asmeurer

Bu, yalnızca iki döngü katmanıyla sınırlı görünüyor. Üç iç içe döngüye sahip bazı kodları güncellemem gerekiyor ve yeni bir müşteri gereksinimi, belirli koşullar altında en içteki döngünün en dıştaki döngünün bir sonraki yinelemesine devam etmesi gerektiği anlamına geliyor. Önerinizin bu senaryo için geçerli olmayacağını varsayıyorum.
kasperd

42

Diğer dillerde, döngüyü etiketleyebilir ve etiketli döngüden kopabilirsiniz. Python Geliştirme Önerisi (PEP) 3136, bunları Python'a eklemeyi önerdi, ancak Guido bunu reddetti :

Ancak, bu özelliği gerektirecek kadar karmaşık bir kodun çok nadir olması nedeniyle reddediyorum. Çoğu durumda, örneğin 'dönüş' kullanarak temiz kod üreten mevcut çözüm yolları vardır. Geri dönüşü kullanmayı mümkün kılan bir yeniden düzenleme işleminden kodun netliğinin zarar görebileceği bazı (nadir) gerçek durumlar olduğundan eminim, ancak bu iki sorunla dengeleniyor:

  1. Karmaşıklık dile kalıcı olarak eklendi. Bu sadece tüm Python uygulamalarını değil, aynı zamanda her kaynak analiz aracını ve elbette dil için tüm dokümantasyonu etkiler.

  2. Özelliğin doğru kullanılacağından daha fazla kötüye kullanılacağına dair beklentim, kod netliğinde net bir düşüşe yol açacak (bundan sonra yazılan tüm Python kodlarında ölçülmüştür). Tembel programcılar her yerdedir ve siz farkına varmadan ellerinizde anlaşılmaz kodlardan inanılmaz bir karmaşa var.

Yani eğer şansınız kalmamışsa bunu umuyorsunuz, ama orada iyi seçenekler olduğu için diğer cevaplardan birine bakın.


4
İlginç. Guido'ya burada katılıyorum. Bazı durumlarda iyi olsa da kötüye kullanılacaktır. Uygulanmamasının bir başka nedeni de şu anda kodu C ve Python arasında ileri geri taşımanın oldukça basit olmasıdır. Python diğer dillerde eksik olan özellikleri almaya başladığında bu daha da zorlaşır. Örneğin, Python'da bir for döngüsü üzerinde başka bir ifadeye sahip olabileceğiniz gerçeğini ele alalım ... bu, kodu diğer diller için daha az taşınabilir hale getirir.
eric.frederich

2
Yaşasın Guido eden BDFL
JnBrymn

4
Bu, iyi bir karşı argümandan daha çok kırmızı bir iddiadır, ancak bana öyle geliyor ki, davranışı for-elseadlandırılmış döngülerden daha karmaşık, okunması daha zor ve muhtemelen daha fazla kötüye kullanılıyor (düpedüz bir hata değilse). Sanırım bundan farklı bir anahtar kelime kullanırdım else- belki de böyle bir şey resumeiyi olurdu? Sen breakdöngüde ve resumeondan sonra doğru?
ArtOfWarfare

5
Bu beni üzüyor. Aynı anda Python'u ne kadar sevdiğime ve ondan nefret ettiğime inanamıyorum. Çok güzel, ama yine de öyle.
jlh

5
@jlh Benim için çoğunlukla wtf. Bazen meşru amaç için farklı olmak değil, sadece farklı olmak istediğini düşünüyorum. Bu buna güzel bir örnek. Dış döngüleri sık sık kırma ihtiyacıyla karşılaşıyorum.
Rikaelus

14

Sanırım böyle bir şey yapabilirsin:

for ii in range(200):
    restart = False
    for jj in range(200, 400):
        ...block0...
        if something:
            restart = True
            break
    if restart:
        continue
    ...block1...

4
-1: OP, böyle bir şey yapabileceklerini bildiklerini açıkça belirtti ve bu, kabul edilen cevabın daha karmaşık bir versiyonuna benziyor (ki bu, sizinkinden 8 ay öncesine dayanıyor, bu yüzden kabul edileni kaçırmış olamazsınız. Cevap).
ArtOfWarfare

10
Kabul cevabı hiç görmediğin değilse nettir for, elsedaha önce (ve nasıl çalıştığını kendi başının üst kapalı hatırlamıyorum bile çoğu insanı düşünün).
asmeurer

3

Bence bunu başarmanın en kolay yollarından biri "devam et" i "ara" ile değiştirmektir, yani

for ii in range(200):
 for jj in range(200, 400):
    ...block0...
    if something:
        break
 ...block1...       

Örneğin, tam olarak nasıl gittiğini görmek için işte kolay kod:

for i in range(10):
    print("doing outer loop")
    print("i=",i)
    for p in range(10):
        print("doing inner loop")
        print("p=",p)
        if p==3:
            print("breaking from inner loop")
            break
    print("doing some code in outer loop")

2

Bu tür bir sorunu çözmenin başka bir yolu da Exception () kullanmaktır.

for ii in range(200):
    try:
        for jj in range(200, 400):
            ...block0...
            if something:
                raise Exception()
    except Exception:
        continue
    ...block1...

Örneğin:

for n in range(1,4):
    for m in range(1,4):
        print n,'-',m

sonuç:

    1-1
    1-2
    1-3
    2-1
    2-2
    2-3
    3-1
    3-2
    3-3

M = 3 ise m döngüsünden dış n döngüsüne atlamak istediğimizi varsayarsak:

for n in range(1,4):
    try:
        for m in range(1,4):
            if m == 3:
                raise Exception()            
            print n,'-',m
    except Exception:
        continue

sonuç:

    1-1
    1-2
    2-1
    2-2
    3-1
    3-2

Referans bağlantısı: http://www.programming-idioms.org/idiom/42/continue-outer-loop/1264/python


1

Bir şey bulmak ve sonra içsel yinelemeyi durdurmak istiyoruz. Bayrak sistemi kullanıyorum.

for l in f:
    flag = True
    for e in r:
        if flag==False:continue
        if somecondition:
            do_something()
            flag=False

Çözümünüzün neden reddedildiğini bilmiyorum; Birisi temelde aynı şeyleri yayınlanmıştır ve 10 kez upvoted var
Locane

Stackoverflow konusunda pek şanslı değilim.
Esther

1
Belki de temelde aynı şey zaten burada yayınlandığından, sanırım ... Ve False:continuemesele şu ki ... alışılmadık biçimlendirme. Üstel bilginin norm olduğu "doğal" sistemlerde sıklıkla olduğu gibi, önemli miktarda itibar puanı biriktirmek için SO'da yalnızca birkaç kez şanslı olmanız gerekir. Her neyse, benim "en iyi" yanıtlarım genellikle en az popüler olanlardır.
user7610

0

Ben sadece böyle bir şey yaptım. Bunun için benim çözümüm, iç for döngüsünü bir liste anlayışıyla değiştirmekti.

for ii in range(200):
    done = any([op(ii, jj) for jj in range(200, 400)])
    ...block0...
    if done:
        continue
    ...block1...

burada op, ii ve jj'nin bir kombinasyonuna göre hareket eden bir boole operatörüdür. Benim durumumda, işlemlerden herhangi biri doğru döndüyse, bitirdim.

Bu, kodu bir işleve bölmekten gerçekten farklı değil, ancak mantıksal OR yapmak için "herhangi" operatörünü kullanmanın ve mantığı tek bir satırda yapmanın ilginç olduğunu düşündüm. Aynı zamanda işlev çağrısını da önler.

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.