Lua'nın neden “devam” ifadesi yok?


144

Son birkaç aydır Lua ile çok uğraşıyorum ve özelliklerin çoğunu gerçekten çok seviyorum, ancak bunlar arasında hala bir şey eksik:

  • Neden hayır continue?
  • Bunun için hangi çözümler var?

12
Bu soru sorulduğundan Lua, gotodevam etmek için kullanılabilecek bir açıklama aldı . Aşağıdaki cevaplara bakın.
lhf

Yanıtlar:


71

Lua 5.2'de en iyi çözüm goto kullanmaktır:

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

Bu sürüm 2.0.1'den beri LuaJIT'de desteklenmektedir


47
Umarım gerçek continuebir gün içerirler . gotoYedek çok güzel bakmak ve daha hatları ihtiyacı yoktur. Ayrıca, bunu bir işlevde birden fazla döngü yapsaydı sorun yaratmaz ::continue::mıydı? Her döngü için bir ad oluşturmak iyi bir şey gibi gelmiyor.
ET

66

Dilin sözcüksel kapsamı yönetme biçimi hem gotove ile ilgili sorunlar oluşturur continue. Örneğin,

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

local aDöngü gövdesinin içindeki bildirimi adlı dış değişkeni maskeler ve abu yerelin kapsamı untilifadenin koşulu boyunca uzanır, böylece koşul en içteki testi yapar a.

Varsa continue, semantik olarak ancak koşulda kullanılan tüm değişkenler kapsama girdikten sonra geçerli olması için kısıtlanması gerekir. Bu, kullanıcıyı belgelemek ve derleyicide uygulamak zor bir durumdur. Bu soruna geçici muhtelif öneriler izin vermeme basit cevabı dahil tartışılmıştır continueile repeat ... untildöngü tarzında. Şimdiye kadar hiçbirinin dilde yer almasını sağlamak için yeterince zorlayıcı bir kullanım durumu olmadı.

Geçici çözüm genellikle a'nın continueyürütülmesine neden olacak koşulu tersine çevirmek ve bu durumda döngü gövdesinin geri kalanını toplamaktır. Yani, aşağıdaki döngü

-- 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

yazılabilir

-- 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

Döngü işlemini kontrol eden bir dizi ayrıntılı cull'ınız yoksa, yeterince açıktır ve genellikle bir yük değildir.


5
Bir python arka planından gelen bu kafa karıştırıcı bir cevaptır, çünkü oradaki her kapsam çalıştırmadan önce yerel değişkenlerinin ne olduğunu zaten biliyor. Yani ulaşma durumunda bağlı olmayan bir yerel değişken hatası bekledim until....
ubershmekel

2
Lua topluluğuna girmeden önce Lua topluluğunda bununla ilgili çok fazla tartışma vardı goto. Doğal olarak, gotoaynı meseleye sahiptir. Sonunda, çalışma zamanı ve / veya kod oluşturma maliyetleri ne olursa olsun, ona karşı koruma sağlamak için her ne olursa olsun, gotohem continueçok seviyeli hem de taklit etmek için kullanılabilecek bir esnekliğe sahip olmanın faydalarına değdiğine karar verdiler break. Ayrıntıları almak için Lua liste arşivlerinde ilgili dizileri aramanız gerekir . Onlar tanıttıklarından beri gotoaşılmaz bir şey değildi.
RBerteig

72
Devam etmeden kod yazma konusunda "yeterince açık" bir şey yok. Kodu bir devam işleminin kullanılması gereken bir koşul içine yerleştirmek acemi bir hatadır ve böyle çirkin kod yazma ihtiyacı herhangi bir sempati almamalıdır. Kesinlikle mazeret yok.
Glenn Maynard

4
Bu açıklama bir anlam ifade etmiyor. localyalnızca derleyici direktifidir - hangi çalışma zamanı insructions localve değişken kullanım arasında olduğu önemli değildir - aynı kapsam belirleme davranışını sürdürmek için derleyicide hiçbir şeyi değiştirmeniz gerekmez. Evet, bu çok açık olmayabilir ve bazı ek belgelere ihtiyaç duyabilir, ancak tekrarlamak için derleyicide SIFIR değişiklikleri gerektirir. repeat do break end until truecevabım örnek zaten üretir tam derleyici ile devam edeceğini aynı bayt kodu, tek fark ile ki continueçirkin ekstra sözdizimi gerek olmazdı kullanmak.
Oleg

7
İç değişkeni test edebileceğiniz kusurlu tasarım hakkında konuşur. Koşul iç kapsamın dışındadır ve içindeki değişkenlere erişimi olmamalıdır. C: do{int i=0;}while (i == 0);başarısız veya C ++: eşdeğer de başarısız olduğunu düşünün do int i=0;while (i==0);("bu kapsamda bildirilmedi"). Maalesef şimdi Lua'da bunu değiştirmek için çok geç.
Pedro Gimeno

47

Döngü gövdesini ek olarak sarın repeat until trueve sonra do break enddevam etmenin etkisi için içeride kullanabilirsiniz . Doğal olarak, gerçekten breakde döngüden çıkmayı düşünüyorsanız ek bayraklar ayarlamanız gerekir .

Bu, her seferinde 1, 2 ve 3 yazdırarak 5 kez dönecektir.

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

Bu yapı JMP, Lua bayt kodundaki gerçek bir opcode bile anlamına geliyor !

$ 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
Bu cevap güzel, ama yine de bir yerine 3 satır gerektiriyor. ("devam" düzgün destekleniyorsa) Bir goto etiketinden biraz daha güzel ve güvenlidir, çünkü bu ad için iç içe döngüler için çakışmalardan kaçınılması gerekebilir.
ET

3
Bununla birlikte, her psuedo-continue için yeni bir tanımlayıcı / etiket icat etmek zorunda kalmamanız ve kodun zaman içinde değiştirilmesinden dolayı daha az hataya yatkın olması nedeniyle goto ile ilgili "gerçek" sorundan kaçınır. devam etmenin yararlı olacağını kabul ediyorum , ama bu IMO bir sonraki en iyi şey (ve gerçekten tekrar / iki resmi daha devam "resmi" kadar iki satır gerektirir; .. ve hatta o zaman, eğer çizgi ile ilgili olsaydı her zaman "tekrar yap" ve "gerçek sona kadar" yazabileceğiniz sayılar , örneğin: gist.github.com/wilson0x4d/f8410719033d1e0ef771 )
Shaun Wilson

1
İnsanların aslında performansı dikkate aldıklarını ve hatta SO'ya luacçıktı sağladığını görmek güzel ! Have a well desved upvote :)
DarkWiiPlayer

17

Doğrudan Lua'nın tasarımcısından :

"Devam" ile ilgili temel kaygımız, (bizim görüşümüze göre) "devam" kadar az ya da çok önemli olan ve hatta yerini alabilecek başka kontrol yapıları olmasıdır. (Örneğin, [Java'da olduğu gibi] etiketleriyle kırın veya daha genel bir goto.) "Devam", daha fazla dilde mevcut olması dışında, diğer kontrol yapısı mekanizmalarından daha özel görünmemektedir. (Perl aslında iki "devam" ifadesine sahiptir, "sonraki" ve "yinele". Her ikisi de yararlıdır.)


5
Kabul ediyorum: "Her ikisi de yararlı", "yapmayacağız" açıklamasından hemen sonra
David Ljung Madison Stellar

2
Onlar o kapsamını not etmekti edildi onlar ne zaman adrese bakarak yaptım bunu 5.2'de bir "git" yapısını (ki bu cevap yazıldığında tahliye edilmediğini) ekleyerek,. 5.2.0 yayımlandıktan sonra 2012'den bu yanıta bakın .
Stuart P. Bentley

3
Doğru - çünkü 'git', iyi bir programlama yapısı olarak iyi bilinir. Ah aptallık.
David Ljung Madison Stellar

2
Ama " continueLua'ya koymayı unuttum , üzgünüm " den daha mantıklı gelmedi .
neoedmund

17

İlk bölüm SSS'de öldürüldüğü gibi cevaplanmıştır .

Geçici bir çözüme gelince, döngünün gövdesini bir işlevde ve bundan returnerken, örneğin ör.

-- 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

Veya her ikisini de breakve continueişlevsellik istiyorsanız , yerel işlevin testi gerçekleştirmesini sağlayın, örn.

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
Lütfen yapma. Her yinelemede kapatma ortamı oluşturursunuz ve bu BÜYÜK bellek ve GC döngüleri israfıdır.
Oleg V.Volkov

4
collectgarbage("count")basit 100 denemenizden sonra bile kontrol edin, sonra konuşacağız. Bu tür "erken" optimizasyon, bir yüksek yük projesini geçen hafta her dakika yeniden başlatmaktan kurtardı.
Oleg V.Volkov

4
@ OlegV.Volkov bu örnek GC üzerinde nispeten yüksek bir yük koyarken, sızıntı yapmaz - Tüm geçici kapanışlar toplanacaktır. Projenizi bilmiyorum ama IME'nin tekrar eden çoğu yeniden başlatması sızıntılardan kaynaklanıyor.
finnw

10

Lua'yı daha önce hiç kullanmadım, ama Googled ve bununla geldim:

http://www.luafaq.org/

Soru 1.26'yı kontrol edin .

Bu yaygın bir şikayettir. Lua yazarları, devam etmenin olası yeni kontrol akış mekanizmalarından sadece biri olduğunu hissettiler (tekrar / kapsam kurallarıyla çalışamaması ikincil bir faktördü.)

Lua 5.2'de, aynı işi yapmak için kolayca kullanılabilecek bir goto ifadesi vardır.


8

Aşağıdaki gibi başarabiliriz, hatta sayıları atlar

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

Bu senaryo ile birçok kez karşılaştık ve devam etmeyi simüle etmek için bir bayrak kullanıyoruz. Goto ifadelerinin kullanımından da kaçınmaya çalışıyoruz.

Örnek: Kod, i = 3 dışında i = 1'den i = 10'a kadar olan ifadeleri yazdırmayı amaçlamaktadır. Ayrıca, kodunuzda bulunan diğer iç içe ifadeleri simüle etmek için "loop start", loop end "," start "ve" if end "de yazdırır.

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

geri kalan tüm ifadelerin bir test bayrağıyla döngünün sonuna kadar eklenmesiyle elde edilir.

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

Bunun en iyi yaklaşım olduğunu söylemiyorum ama bizim için mükemmel çalışıyor.


5

Lua, olabildiğince küçülmek isteyen hafif bir betik dilidir. Örneğin, öncesi / sonrası artış gibi birçok tekli işlem kullanılamaz

Devam etmek yerine,

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

Yine ters çevirme ile aşağıdaki kodu kullanabilirsiniz:

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

Tersine çevirme ile ilgili sorun, bir dizide birden fazla koşulun olmamasından daha sıktır (kullanıcı girişini doğrulamak gibi). Ve yol boyunca herhangi bir noktada kısa devre olması gerekebileceğinden, tersine çevirme koşullu terimleri sürekli olarak yuvalamak zorunda kalmak anlamına gelir ("bu kötü mü? Sonra kaçmak; başka kötü mi? Sonra kaçmak" yerine, "Bu iyi mi? o zaman bu iyi mi? o zaman bu iyi mi? o zaman bunu yap" gibi bir kodla sonlandırıyorsunuz
Leslie Krause

-2

Neden devam yok?

Çünkü gereksiz¹. Bir geliştiricinin buna ihtiyaç duyacağı çok az durum vardır.

A) Çok basit bir döngüye sahip olduğunuzda, 1 ya da 2 astarlı, döngü döngüsünü döndürebilirsiniz ve yine de okunabilir.

B) Basit yordamsal kod yazarken (yani geçen yüzyılda nasıl kod yazdığımız), ayrıca yapılandırılmış programlama (yani geçen yüzyılda nasıl daha iyi kod yazdığımız) uygulamalısınız.

C) Nesneye yönelik kod yazıyorsanız, döngü gövdesiniz, bir veya iki satırda ifade edilemediği sürece, en fazla bir veya iki yöntem çağrısından oluşmamalıdır (bu durumda, bkz. A).

D) İşlevsel kod yazıyorsanız, sonraki yineleme için basit bir kuyruk çağrısı döndürmeniz yeterlidir.

Bir continueanahtar kelime kullanmak istediğinizde, Lua'yı python gibi kodlamak istiyorsanız, ki bu sadece değildir.

Bunun için hangi çözümler var?

A) uygulanmadığı sürece, bu durumda herhangi bir geçici çözüme gerek yoktur, Yapısal, Nesneye Dayalı veya Fonksiyonel programlama yapmanız gerekir. Bunlar Lua'nın inşa edildiği paradigmalar, bu yüzden kalıplarından kaçınmak için yolunuzdan çıkarsanız dile karşı savaşıyorsunuz.


Bazı açıklamalar:

Ua Lua çok minimalist bir dildir. Kaçabileceği kadar az özelliğe sahip olmaya çalışır ve bir continueifade bu anlamda önemli bir özellik değildir.

Bu minimalizm felsefesinin bu 2019 röportajında Roberto Ierusalimschy tarafından iyi yakalandığını düşünüyorum :

bunu ve bunu ekleyin ve bunu ortaya koyun ve sonunda nihai sonucun çoğu insanı tatmin etmeyeceğini anlıyoruz ve herkesin istediği tüm seçenekleri koymayacağız, bu yüzden hiçbir şey koymuyoruz. Sonunda, katı mod makul bir uzlaşmadır.

² Diğer dillerden Lua'ya gelen büyük miktarda programcı var gibi görünüyor çünkü komut dosyası oluşturmaya çalıştıkları program ne olursa olsun bunu kullanıyor ve birçoğu kendi dillerinden başka bir şey yazmak istemiyor gibi görünüyor "Lua'da neden X özelliği yok?"

Matz , yakın tarihli bir röportajda Ruby ile benzer bir durumu şöyle anlattı :

En popüler soru şudur: "X dil toplumundanım; X dilinden Ruby diline bir özellik tanıtamaz mısınız?" Veya bunun gibi bir şey. Ve bu isteklere her zamanki cevabım… “hayır, bunu yapmam” çünkü farklı dil tasarımı ve farklı dil geliştirme politikalarımız var.

³ Bu konuda yolunuzu kesmenin birkaç yolu vardır; bazı kullanıcılar, gotoçoğu durumda yeterince iyi bir yaklaşım olan, ancak çok çirkinleşir ve iç içe döngülerle tamamen kırılır. S kullanmanız goto, kodunuzu başkalarına her gösterdiğinizde SICP'nin bir kopyasının size atılma tehlikesini de beraberinde getirir.


1
İlk cümlenin yanlış olduğu ve cevabın geri kalanının yararsız olduğu için aşağı indirdim.
bfontaine

Yararsız'ı? Olabilir; biraz fikir tabanlı bir cevap. İlk cümle açıkça doğrudur; continueuygun bir özellik olabilir, ama bu yapmaz gerekli . Birçok insan Lua'yı onsuz iyi kullanır, bu nedenle herhangi bir programlama Dili için gerekli olmayan düzgün bir özellikten başka bir şey olması gerçekten hiçbir durum yoktur.
DarkWiiPlayer

Bu bir argüman değil: İnsanların başka seçenekleri olmadığı zaman "onsuz iyi" olduklarını iddia edemezsiniz.
bfontaine

Sanırım o zaman sadece farklı "gerekli" tanımlarımız var.
DarkWiiPlayer
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.