Mengapa Lua tidak memiliki pernyataan “terus”?


144

Saya telah banyak berurusan dengan Lua dalam beberapa bulan terakhir, dan saya sangat menyukai sebagian besar fitur tetapi saya masih kehilangan sesuatu di antara mereka:

  • Kenapa tidak ada continue?
  • Penanganan apa yang ada untuk itu?

12
Karena pertanyaan ini diajukan, Lua mendapat gotopernyataan yang dapat digunakan untuk mengimplementasikan melanjutkan. Lihat jawabannya di bawah.
lhf

Jawaban:


71

Dalam Lua 5.2 solusi terbaik adalah menggunakan goto:

-- prints odd numbers in [|1,10|]
for i=1,10 do
  if i % 2 == 0 then goto continue end
  print(i)
  ::continue::
end

Ini didukung di LuaJIT sejak versi 2.0.1


47
Saya berharap mereka memasukkan hari yang sebenarnya continue. The gotopengganti tidak terlihat sangat bagus dan membutuhkan lebih banyak garis. Juga, bukankah itu akan menimbulkan masalah jika Anda memiliki lebih dari satu loop melakukan ini dalam satu fungsi, keduanya dengan ::continue::? Membuat nama per loop tidak terdengar seperti hal yang layak untuk dilakukan.
ET

66

Cara bahasa mengelola ruang lingkup leksikal menciptakan masalah dengan menyertakan keduanya gotodan continue. Sebagai contoh,

local a=0
repeat 
    if f() then
        a=1 --change outer a
    end
    local a=f() -- inner a
until a==0 -- test inner a

Deklarasi local adi dalam loop body menutupi variabel luar bernama a, dan cakupan lokal itu meluas di seluruh kondisi untilpernyataan sehingga kondisi sedang menguji yang paling dalam a.

Jika continueada, itu harus dibatasi secara semantik hanya valid setelah semua variabel yang digunakan dalam kondisi tersebut masuk ke dalam ruang lingkup. Ini adalah kondisi yang sulit untuk didokumentasikan kepada pengguna dan ditegakkan dalam kompiler. Berbagai proposal seputar masalah ini telah dibahas, termasuk jawaban sederhana penolakan continuedengan repeat ... untilgaya loop. Sejauh ini, tidak ada yang memiliki kasus penggunaan yang cukup menarik untuk memasukkan mereka ke dalam bahasa.

Pekerjaan sekitar umumnya untuk membalikkan kondisi yang akan menyebabkan a continuedieksekusi, dan mengumpulkan sisa tubuh loop di bawah kondisi itu. Jadi, loop berikut

-- not valid Lua 5.1 (or 5.2)
for k,v in pairs(t) do
  if isstring(k) then continue end
  -- do something to t[k] when k is not a string
end

dapat ditulis

-- valid Lua 5.1 (or 5.2)
for k,v in pairs(t) do
  if not isstring(k) then 
    -- do something to t[k] when k is not a string
  end
end

Ini cukup jelas, dan biasanya bukan beban kecuali Anda memiliki serangkaian pemusnahan rumit yang mengontrol operasi loop.


5
Berasal dari latar belakang python, ini adalah jawaban yang membingungkan karena setiap ruang lingkup di sana sudah tahu apa variabel lokalnya sebelum dijalankan. Yaitu saya mengharapkan kesalahan variabel lokal tidak terikat dalam hal mencapai until....
ubershmekel

2
Ada banyak diskusi tentang hal ini di komunitas Lua sebelum pengenalan gotoke dalam Lua 5.2. Secara alami, gotomemiliki masalah yang sama. Mereka akhirnya memutuskan bahwa apa pun biaya runtime dan / atau pembuatan kode untuk melindunginya tidak sepadan dengan manfaat memiliki fleksibel gotoyang dapat digunakan untuk meniru keduanya continuedan multi-level break. Anda harus mencari arsip daftar Lua untuk utas yang relevan untuk mendapatkan detailnya. Karena mereka memang memperkenalkan goto, itu jelas tidak dapat diatasi.
RBerteig

72
Tidak ada yang "cukup jelas" tentang penulisan kode tanpa melanjutkan. Ini adalah kesalahan pemula untuk kode sarang di dalam kondisi di mana lanjutan seharusnya telah digunakan, dan kebutuhan untuk menulis kode jelek seperti itu seharusnya tidak menerima simpati. Sama sekali tidak ada alasan.
Glenn Maynard

4
Penjelasan ini tidak masuk akal. localadalah arahan hanya kompiler - tidak peduli apa pun runtime insructions berada di antara localdan penggunaan variabel - Anda tidak perlu mengubah apa pun di kompiler untuk mempertahankan perilaku pelingkupan yang sama. Ya, ini mungkin tidak begitu jelas dan memerlukan beberapa dokumentasi tambahan, tetapi, untuk mengulangi lagi, diperlukan perubahan NOL dalam kompiler. repeat do break end until truecontoh dalam jawaban saya sudah menghasilkan bytecode yang sama persis bahwa compiler akan melanjutkan, satu-satunya perbedaan adalah bahwa dengan continueAnda tidak perlu sintaks tambahan yang jelek untuk menggunakannya.
Oleg V. Volkov

7
Anda dapat menguji variabel dalam berbicara tentang desain yang cacat. Kondisi di luar lingkup dalam dan seharusnya tidak memiliki akses ke variabel di dalamnya. Pertimbangkan padanan dalam C: do{int i=0;}while (i == 0);gagal, atau dalam C ++: do int i=0;while (i==0);juga gagal ("tidak dideklarasikan dalam lingkup ini"). Terlambat untuk mengubahnya sekarang di Lua, sayangnya.
Pedro Gimeno

47

Anda dapat membungkus lingkaran tubuh di tambahan repeat until truedan kemudian gunakan do break enddi dalam untuk efek melanjutkan. Secara alami, Anda perlu mengatur flag tambahan jika Anda juga bermaksud untuk benar-benar breakkeluar dari loop.

Ini akan berulang 5 kali, mencetak 1, 2, dan 3 setiap kali.

for idx = 1, 5 do
    repeat
        print(1)
        print(2)
        print(3)
        do break end -- goes to next iteration of for
        print(4)
        print(5)
    until true
end

Konstruksi ini bahkan diterjemahkan menjadi opcode literal JMPdalam bytecode Lua!

$ luac -l continue.lua 

main <continue.lua:0,0> (22 instructions, 88 bytes at 0x23c9530)
0+ params, 6 slots, 0 upvalues, 4 locals, 6 constants, 0 functions
    1   [1] LOADK       0 -1    ; 1
    2   [1] LOADK       1 -2    ; 3
    3   [1] LOADK       2 -1    ; 1
    4   [1] FORPREP     0 16    ; to 21
    5   [3] GETGLOBAL   4 -3    ; print
    6   [3] LOADK       5 -1    ; 1
    7   [3] CALL        4 2 1
    8   [4] GETGLOBAL   4 -3    ; print
    9   [4] LOADK       5 -4    ; 2
    10  [4] CALL        4 2 1
    11  [5] GETGLOBAL   4 -3    ; print
    12  [5] LOADK       5 -2    ; 3
    13  [5] CALL        4 2 1
    14  [6] JMP         6   ; to 21 -- Here it is! If you remove do break end from code, result will only differ by this single line.
    15  [7] GETGLOBAL   4 -3    ; print
    16  [7] LOADK       5 -5    ; 4
    17  [7] CALL        4 2 1
    18  [8] GETGLOBAL   4 -3    ; print
    19  [8] LOADK       5 -6    ; 5
    20  [8] CALL        4 2 1
    21  [1] FORLOOP     0 -17   ; to 5
    22  [10]    RETURN      0 1

4
Jawaban ini bagus, tetapi masih membutuhkan 3 baris, bukan hanya satu. (jika "terus" didukung dengan benar) Ini sedikit lebih cantik dan lebih aman daripada label goto, karena untuk itu nama bentrokan mungkin perlu dihindari untuk loop bersarang.
ET

3
itu, bagaimanapun, menghindari masalah "nyata" dengan goto di mana Anda tidak perlu membuat pengenal / label baru untuk setiap psuedo-continue dan itu kurang rawan kesalahan karena kode dimodifikasi dari waktu ke waktu. Saya setuju bahwa terus akan berguna , tetapi IMO ini adalah hal terbaik berikutnya (dan itu benar-benar membutuhkan dua baris untuk pengulangan / sampai vs. "melanjutkan;" yang lebih formal .. dan bahkan kemudian, jika Anda yang peduli dengan garis Hitungan Anda selalu dapat menulis "jangan ulangi" dan "sampai akhir yang sebenarnya", misalnya: gist.github.com/wilson0x4d/f8410719033d1e0ef771 )
Shaun Wilson

1
Senang melihat orang-orang benar-benar mempertimbangkan kinerja dan bahkan memberikan luacoutput pada SO! Selamat menikmati :):
DarkWiiPlayer

17

Langsung dari desainer Lua sendiri :

Perhatian utama kami dengan "melanjutkan" adalah bahwa ada beberapa struktur kontrol lain yang (dalam pandangan kami) lebih atau kurang sama pentingnya dengan "melanjutkan" dan bahkan mungkin menggantinya. (Misalnya, putus dengan label [seperti di Jawa] atau bahkan goto yang lebih umum.) "Terus" tampaknya tidak lebih istimewa daripada mekanisme struktur kontrol lainnya, kecuali bahwa ia hadir dalam lebih banyak bahasa. (Perl sebenarnya memiliki dua pernyataan "lanjutkan", "berikutnya" dan "ulang". Keduanya berguna.)


5
Saya suka tiket masuk: "Keduanya berguna" tepat setelah penjelasan tentang "kami tidak akan melakukannya"
David Ljung Madison Stellar

2
Itu untuk dicatat lingkup bahwa mereka sedang mencari untuk alamat ketika mereka melakukannya melakukannya, dengan menambahkan "goto" membangun di 5.2 (yang belum dirilis saat jawaban ini ditulis). Lihat jawaban ini dari 2012 , setelah 5.2.0 dirilis.
Stuart P. Bentley

3
Benar - karena 'goto' dikenal dengan baik sebagai konstruk pemrograman yang layak. (akhiri sarkasme) Ah, baiklah.
David Ljung Madison Stellar

2
Tapi itu kedengarannya tidak masuk akal daripada "Aku hanya lupa memasukkannya continueke dalam Lua, maaf."
neoedmund

17

Bagian pertama dijawab di FAQ sebagai dibunuh keluar runcing.

Adapun solusinya, Anda bisa membungkus tubuh loop dalam suatu fungsi dan returnawal dari itu, misalnya

-- Print the odd numbers from 1 to 99
for a = 1, 99 do
  (function()
    if a % 2 == 0 then
      return
    end
    print(a)
  end)()
end

Atau jika Anda ingin keduanya breakdan continuefungsionalitas, mintalah fungsi lokal melakukan tes, mis

local a = 1
while (function()
  if a > 99 then
    return false; -- break
  end
  if a % 2 == 0 then
    return true; -- continue
  end
  print(a)
  return true; -- continue
end)() do
  a = a + 1
end

16
Tolong jangan. Anda membuat lingkungan penutupan pada setiap iterasi dan ini adalah pemborosan besar memori dan siklus GC.
Oleg V. Volkov

4
periksa collectgarbage("count")bahkan setelah 100 percobaan sederhana Anda dan kemudian kami akan berbicara. Optimalisasi "prematur" seperti itu menyelamatkan satu proyek highload dari reboot setiap menit minggu lalu.
Oleg V. Volkov

4
@ OlegV.Volkov sementara contoh ini tidak menempatkan beban yang relatif tinggi pada GC, tidak bocor - Semua penutupan sementara akan dikumpulkan. Saya tidak tahu tentang proyek Anda tetapi kebanyakan reboot yang berulang karena IME bocor.
finnw

10

Saya belum pernah menggunakan Lua sebelumnya, tetapi saya mencari di Google dan menghasilkan ini:

http://www.luafaq.org/

Periksa pertanyaan 1.26 .

Ini adalah keluhan umum. Para penulis Lua merasa bahwa terus hanyalah salah satu dari sejumlah mekanisme aliran kontrol baru yang mungkin (fakta bahwa ia tidak dapat bekerja dengan aturan lingkup pengulangan / sampai adalah faktor sekunder.)

Dalam Lua 5.2, ada pernyataan goto yang dapat dengan mudah digunakan untuk melakukan pekerjaan yang sama.


8

Kita dapat mencapainya seperti di bawah ini, ia akan melewati angka genap

local len = 5
for i = 1, len do
    repeat 
        if i%2 == 0 then break end
        print(" i = "..i)
        break
    until true
end

O / P:

i = 1
i = 3
i = 5

6

Kami mengalami skenario ini berkali-kali dan kami hanya menggunakan bendera untuk mensimulasikan melanjutkan. Kami mencoba untuk menghindari penggunaan pernyataan goto juga.

Contoh: Kode ini bermaksud untuk mencetak pernyataan dari i = 1 ke i = 10 kecuali i = 3. Selain itu ia juga mencetak "loop start", loop end "," if start ", dan" if end "untuk mensimulasikan pernyataan bersarang lainnya yang ada dalam kode Anda.

size = 10
for i=1, size do
    print("loop start")
    if whatever then
        print("if start")
        if (i == 3) then
            print("i is 3")
            --continue
        end
        print(j)
        print("if end")
    end
    print("loop end")
end

dicapai dengan melampirkan semua pernyataan yang tersisa sampai ruang lingkup akhir loop dengan bendera tes.

size = 10
for i=1, size do
    print("loop start")
    local continue = false;  -- initialize flag at the start of the loop
    if whatever then
        print("if start")
        if (i == 3) then
            print("i is 3")
            continue = true
        end

        if continue==false then          -- test flag
            print(j)
            print("if end")
        end
    end

    if (continue==false) then            -- test flag
        print("loop end")
    end
end

Saya tidak mengatakan bahwa ini adalah pendekatan terbaik tetapi itu bekerja dengan baik bagi kami.


5

Lua adalah bahasa scripting ringan yang ingin mengecil mungkin. Misalnya, banyak operasi unary seperti kenaikan sebelum / sesudah tidak tersedia

Alih-alih melanjutkan, Anda bisa menggunakan goto like

arr = {1,2,3,45,6,7,8}
for key,val in ipairs(arr) do
  if val > 6 then
     goto skip_to_next
  end
     # perform some calculation
  ::skip_to_next::
end

4

Sekali lagi dengan pembalik, Anda cukup menggunakan kode berikut:

for k,v in pairs(t) do
  if not isstring(k) then 
    -- do something to t[k] when k is not a string
end

Masalah dengan inversi adalah bahwa lebih sering daripada tidak ada beberapa persyaratan dalam suatu seri (seperti untuk memvalidasi input pengguna). Dan karena mungkin perlu ada hubungan pendek di setiap titik di sepanjang jalan, inversi berarti harus bersarang kondisi bersyarat terus menerus (bukan "apakah ini buruk? Lalu melarikan diri; apakah ini buruk? Lalu melarikan diri", yang sangat mudah, Anda berakhir dengan kode seperti "apakah ini baik-baik saja? lalu apakah ini baik-baik saja? lalu apakah ini baik-baik saja? kemudian lakukan ini" yang sangat berlebihan
Leslie Krause

-2

Mengapa tidak ada yang melanjutkan?

Karena itu tidak perlu¹. Ada beberapa situasi di mana seorang dev akan membutuhkannya.

A) Ketika Anda memiliki loop yang sangat sederhana, katakanlah 1 atau 2-liner, maka Anda bisa membalikkan kondisi loop dan masih banyak yang bisa dibaca.

B) Ketika Anda menulis kode prosedural sederhana (alias. Bagaimana kami menulis kode pada abad terakhir), Anda juga harus menerapkan pemrograman terstruktur (alias. Bagaimana kami menulis kode yang lebih baik pada abad terakhir)

C) Jika Anda menulis kode berorientasi objek, badan loop Anda harus terdiri dari tidak lebih dari satu atau dua pemanggilan metode kecuali dapat dinyatakan dalam satu atau dua-liner (dalam hal ini, lihat A)

D) Jika Anda menulis kode fungsional, cukup kembalikan panggilan ekor untuk iterasi berikutnya.

Satu-satunya kasus ketika Anda ingin menggunakan continuekata kunci adalah jika Anda ingin kode Lua seperti itu python, padahal tidak .²

Penanganan apa yang ada untuk itu?

Kecuali A) berlaku, dalam hal ini tidak perlu ada solusi, Anda harus melakukan pemrograman Terstruktur, Berorientasi Objek atau Fungsional. Itulah paradigma yang dibangun Lua, jadi Anda akan berjuang melawan bahasa jika Anda berusaha keras untuk menghindari pola mereka. Their


Beberapa klarifikasi:

¹ Lua adalah bahasa yang sangat minimalis. Itu mencoba untuk memiliki fitur sesedikit mungkin, dan continuepernyataan bukanlah fitur penting dalam arti itu.

Saya pikir filosofi minimalis ini ditangkap dengan baik oleh Roberto Ierusalimschy dalam wawancara tahun 2019 ini :

tambahkan itu dan itu dan itu, keluarkan itu, dan pada akhirnya kita memahami kesimpulan akhir tidak akan memuaskan kebanyakan orang dan kita tidak akan menempatkan semua opsi yang diinginkan semua orang, jadi kita tidak menaruh apa pun. Pada akhirnya, mode ketat adalah kompromi yang masuk akal.

² Tampaknya ada sejumlah besar programmer yang datang ke Lua dari bahasa lain karena program apa pun yang mereka coba skrip untuk menggunakannya, dan banyak dari mereka sepertinya tidak ingin menulis apa pun selain bahasa mereka sendiri. pilihan, yang mengarah ke banyak pertanyaan seperti "Mengapa Lua tidak memiliki fitur X?"

Matz menggambarkan situasi yang sama dengan Ruby dalam sebuah wawancara baru - baru ini :

Pertanyaan paling populer adalah: "Saya dari komunitas bahasa X; tidak bisakah Anda memperkenalkan fitur dari bahasa X ke Ruby?", Atau sesuatu seperti itu. Dan jawaban saya yang biasa untuk permintaan ini adalah ... "tidak, saya tidak akan melakukan itu", karena kami memiliki desain bahasa yang berbeda dan kebijakan pengembangan bahasa yang berbeda.

³ Ada beberapa cara untuk meretas jalan Anda di sekitar ini; beberapa pengguna telah menyarankan untuk menggunakan goto, yang merupakan aproximation cukup baik dalam kebanyakan kasus, tetapi menjadi sangat jelek dengan sangat cepat dan benar-benar rusak dengan loop bersarang. Menggunakan gotos juga menempatkan Anda dalam bahaya memiliki salinan SICP yang dilemparkan kepada Anda setiap kali Anda menunjukkan kode Anda kepada orang lain.


1
Saya tidak memilih karena kalimat pertama jelas salah, dan sisanya jawabannya tidak membantu.
bfontaine

Tidak membantu? Mungkin; itu jawaban yang agak berdasarkan pendapat. Namun kalimat pertama jelas benar; continuemungkin fitur yang nyaman, tetapi itu tidak membuatnya perlu . Banyak orang menggunakan Lua baik-baik saja tanpa itu, jadi benar-benar tidak ada kasus untuk itu selain fitur rapi yang tidak penting untuk bahasa pemrograman apa pun.
DarkWiiPlayer

Itu bukan argumen: Anda tidak dapat berdebat bahwa orang "baik-baik saja tanpa itu" ketika mereka tidak punya pilihan.
bfontaine

Saya pikir kita hanya memiliki definisi "perlu" yang berbeda.
DarkWiiPlayer
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.