Python: Melanjutkan ke iterasi berikutnya di lingkaran luar

135

Saya ingin tahu apakah ada cara built-in untuk melanjutkan iterasi berikutnya di loop luar dengan python. Misalnya, perhatikan kodenya:

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

Saya ingin ini melanjutkan pernyataan untuk keluar dari jj loop dan kebagian item berikutnya dalam ii loop. Saya dapat mengimplementasikan logika ini dengan cara lain (dengan menetapkan variabel flag), tetapi apakah ada cara mudah untuk melakukan ini, atau apakah ini seperti meminta terlalu banyak?

Sahas
sumber
11
Sebenarnya ada pernyataan goto yang berfungsi untuk Python: entrian.com/goto . Itu dirilis sebagai lelucon April bodoh :-), tetapi seharusnya bekerja.
codeape
3
Oh, tolong jangan gunakan goto joke itu! Ini sangat pintar, tetapi Anda akan sedih nanti jika Anda memasukkannya ke dalam kode Anda.
Ned Batchelder

Jawaban:

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

Dalam kasus umum, ketika Anda memiliki beberapa tingkat perulangan dan breaktidak bekerja untuk Anda (karena Anda ingin melanjutkan salah satu loop atas, bukan yang tepat di atas yang sekarang), Anda dapat melakukan salah satu dari yang berikut ini

Perbaiki kembali loop yang ingin Anda hindari dari fungsi

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


for i in ...:
    inner()

Kerugiannya adalah bahwa Anda mungkin perlu beralih ke fungsi baru itu beberapa variabel, yang sebelumnya dalam cakupan. Anda bisa meneruskannya sebagai parameter, menjadikannya variabel instan pada objek (buat objek baru hanya untuk fungsi ini, jika masuk akal), atau variabel global, lajang, apa pun (ehm, ehm).

Atau Anda dapat mendefinisikan innersebagai fungsi bersarang dan membiarkannya menangkap apa yang dibutuhkannya (mungkin lebih lambat?)

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

Gunakan pengecualian

Secara filosofis, inilah pengecualian untuk, memutus aliran program melalui blok bangunan pemrograman terstruktur (jika, untuk, sementara) bila perlu.

Keuntungannya adalah Anda tidak perlu memecah satu bagian kode menjadi beberapa bagian. Ini bagus jika ini semacam perhitungan yang Anda rancang saat menulisnya dengan Python. Memperkenalkan abstraksi pada titik awal ini dapat memperlambat Anda.

Hal buruk dengan pendekatan ini adalah bahwa penulis interpreter / kompiler biasanya berasumsi bahwa pengecualian adalah pengecualian dan mengoptimalkannya.

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

Buat kelas pengecualian khusus untuk ini, sehingga Anda tidak mengambil risiko secara tidak sengaja membungkam beberapa pengecualian lain.

Sesuatu yang sama sekali berbeda

Saya yakin masih ada solusi lain.

pengguna7610
sumber
Tidak percaya saya tidak berpikir untuk memindahkan loop kedua ke metode lain. Saya mulai lambat
pmccallum
1
Bagi saya, menggunakan Pengecualian adalah cara yang baik untuk mencapai ini. Saya setuju dengan @ user7610 - "secara filosofis, inilah pengecualian untuk".
Renato Byrro
Variabel boolean tua yang baik dan pernyataan If?
MrR
OP sedang mencari solusi alternatif untuk itu, "Saya bisa mengimplementasikan logika ini dengan cara lain (dengan menetapkan variabel flag) [...]"
user7610
149
for ii in range(200):
    for jj in range(200, 400):
        ...block0...
        if something:
            break
    else:
        ...block1...

Break akan merusak loop dalam, dan block1 tidak akan dieksekusi (itu hanya akan berjalan jika loop dalam keluar secara normal)

culebrón
sumber
1
Hai, apakah ada opsi lain seperti ini? Karena saya ingin melakukan yang lain untuk loop di block1, dan seperti itu kode saya akan masuk 3 level Situasi yang aneh.
Sahas
3
Bagi saya ini kedengarannya seperti Anda mencoba melakukan sesuatu dengan untuk loop yang akan lebih baik didekati dengan cara yang berbeda ...
Kimvais
Iya. Itu sebabnya saya tidak menggunakan struktur for..else. Sekarang saya masih membutuhkan loop, tapi saya akan menggunakan variabel flag untuk mengalihkan kontrol.
Sahas
3
for...elseseringkali merupakan konstruk yang bermanfaat, meskipun bisa membingungkan. Ingat saja itu elseberarti "tidak ada istirahat" dalam konteks ini.
penanggung jawab
Ini tampaknya terbatas hanya pada dua lapisan loop. Saya perlu memperbarui beberapa kode yang memiliki tiga loop bersarang, dan persyaratan pelanggan baru berarti bahwa dalam keadaan tertentu loop terdalam perlu melanjutkan iterasi berikutnya dari loop terluar. Saya menganggap saran Anda tidak berlaku untuk skenario itu.
kasperd
42

Dalam bahasa lain Anda bisa memberi label loop dan keluar dari loop berlabel. Python Enhancement Proposal (PEP) 3136 menyarankan untuk menambahkan ini ke Python tetapi Guido menolaknya :

Namun, saya menolaknya dengan alasan kode sangat rumit untuk memerlukan fitur ini sangat jarang. Dalam sebagian besar kasus, ada penyelesaian yang menghasilkan kode bersih, misalnya menggunakan 'kembali'. Meskipun saya yakin ada beberapa kasus nyata (jarang) di mana kejelasan kode akan mengalami refactoring yang memungkinkan untuk menggunakan return, ini diimbangi oleh dua masalah:

  1. Kompleksitas ditambahkan ke bahasa, secara permanen. Ini mempengaruhi tidak hanya semua implementasi Python, tetapi juga setiap alat analisis sumber, ditambah tentu saja semua dokumentasi untuk bahasa tersebut.

  2. Harapan saya bahwa fitur tersebut akan disalahgunakan lebih dari yang akan digunakan dengan benar, yang mengarah ke penurunan bersih dalam kejelasan kode (diukur di semua kode Python yang ditulis selanjutnya). Pemrogram malas ada di mana-mana, dan sebelum Anda menyadarinya, Anda memiliki kekacauan luar biasa di tangan Anda dengan kode yang tidak dapat dipahami.

Jadi jika itu yang Anda harapkan, Anda kurang beruntung, tetapi perhatikan salah satu jawaban lain karena ada pilihan bagus di sana.

Dave Webb
sumber
4
Menarik. Saya setuju dengan Guido di sini. Meskipun akan menyenangkan untuk beberapa kasus, itu akan disalahgunakan. Alasan lain untuk tidak mengimplementasikannya adalah bahwa saat ini cukup lurus ke depan untuk kode port bolak-balik antara C dan Python. Setelah Python mulai mengambil fitur yang tidak dimiliki oleh bahasa lain, ini menjadi lebih sulit. Ambil contoh fakta bahwa Anda dapat memiliki pernyataan lain pada for for dalam Python ... ini membuat kode lebih mudah dibawa ke bahasa lain.
eric.frederich
2
Semua menyambut Guido BDFL kami
JnBrymn
4
Ini lebih merupakan red-heiring daripada kontra-argumen yang bagus, tetapi bagi saya kelihatannya perilaku for-elselebih rumit, lebih sulit dibaca, dan mungkin lebih banyak disalahgunakan (jika bukan kesalahan langsung) daripada loop bernama. Saya pikir saya akan menggunakan kata kunci yang berbeda dari else- mungkin sesuatu seperti resumeitu baik? Anda breakdalam lingkaran dan resumetepat setelah itu?
ArtOfWarfare
5
Ini membuatku sedih. Saya tidak bisa percaya betapa saya mencintai dan membenci Python pada saat yang sama. Sangat indah, namun demikian juga.
jlh
5
@ jlh Sebagian besar wtf untuk saya. Terkadang saya pikir itu ingin menjadi berbeda bukan untuk tujuan yang sah tetapi hanya untuk menjadi berbeda. Ini adalah contoh yang bagus untuk itu. Saya menemukan kebutuhan untuk memutuskan loop luar cukup sering.
Rikaelus
14

Saya pikir Anda bisa melakukan sesuatu seperti ini:

for ii in range(200):
    restart = False
    for jj in range(200, 400):
        ...block0...
        if something:
            restart = True
            break
    if restart:
        continue
    ...block1...
penilai
sumber
4
-1: OP dengan jelas menyatakan bahwa mereka tahu mereka bisa melakukan sesuatu seperti ini, dan ini hanya tampak seperti versi yang lebih berantakan dari jawaban yang diterima (yang mendahului Anda selama 8 bulan, jadi tidak mungkin Anda hanya melewatkan yang diterima menjawab).
ArtOfWarfare
10
Jawaban yang diterima tidak jelas jika Anda belum pernah melihat for, elsesebelum (dan saya pikir bahkan sebagian besar orang yang memiliki tidak ingat dari atas kepala mereka cara kerjanya).
penanggung jawab
3

Saya pikir salah satu cara termudah untuk mencapai ini adalah mengganti pernyataan "lanjutkan" dengan "istirahat", yaitu

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

Sebagai contoh, ini adalah kode mudah untuk melihat bagaimana tepatnyanya:

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")
Khelina Fedorchuk
sumber
2

Cara lain untuk menangani masalah seperti ini adalah dengan menggunakan Exception ().

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

Sebagai contoh:

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

hasil:

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

Dengan asumsi kita ingin melompat ke loop n luar dari m loop jika m = 3:

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

hasil:

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

Tautan referensi: http://www.programming-idioms.org/idiom/42/continue-outer-loop/1264/python

Patrick
sumber
1

Kami ingin menemukan sesuatu dan kemudian menghentikan iterasi batin. Saya menggunakan sistem bendera.

for l in f:
    flag = True
    for e in r:
        if flag==False:continue
        if somecondition:
            do_something()
            flag=False
Esther
sumber
Saya tidak tahu mengapa solusi Anda dibatalkan; seseorang memposting pada dasarnya hal yang persis sama dan mendapat 10 kali
terpilih
Saya tidak terlalu beruntung dengan stackoverflow.
Esther
1
Mungkin karena pada dasarnya ada hal yang persis sama yang diposting di sini, saya kira ... Dan False:continuemasalahnya adalah ... format yang tidak biasa. Seperti yang sering terjadi dalam sistem "alami" di mana eksponensial adalah norma, Anda hanya perlu beruntung beberapa kali di SO untuk mengumpulkan sejumlah besar poin reputasi. Bagaimanapun, jawaban "terbaik" saya biasanya yang paling tidak populer.
user7610
0

Saya hanya melakukan sesuatu seperti ini. Solusi saya untuk ini adalah mengganti interior untuk loop dengan pemahaman daftar.

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

di mana op adalah beberapa operator boolean yang bekerja pada kombinasi ii dan jj. Dalam kasus saya, jika salah satu operasi kembali benar, saya selesai.

Ini benar-benar tidak jauh berbeda dari memecah kode menjadi fungsi, tetapi saya berpikir bahwa menggunakan operator "apa pun" untuk melakukan logika ATAU pada daftar boolean dan melakukan logika semua dalam satu baris itu menarik. Ini juga menghindari pemanggilan fungsi.

cagem12
sumber