web-dev-qa-db-ja.com

Luaに「継続」ステートメントがないのはなぜですか?

私は過去数ヶ月間、Luaをたくさん扱ってきました。私はほとんどの機能が本当に好きですが、それらの中でまだ何かが欠けています:

  • continueがないのはなぜですか?
  • それにはどのような回避策がありますか?
132
Dant

言語がレキシカルスコープを管理する方法は、gotocontinueの両方を含めることで問題を引き起こします。例えば、

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 aの宣言は、aという名前の外部変数をマスクし、そのローカルのスコープはuntilステートメントの条件全体に及ぶため、条件は最も内側のaをテストします。

continueが存在する場合は、条件で使用されるすべての変数がスコープに入った後にのみ有効になるように、意味的に制限する必要があります。これは、ユーザーに文書化し、コンパイラーで実施するのが難しい条件です。 repeat ... untilスタイルのループでcontinueを許可しないという簡単な答えなど、この問題に関するさまざまな提案が議論されています。これまでのところ、十分に説得力のあるユースケースを持っていなかったため、それらを言語に含めることはできませんでした。

回避策は、通常、continueを実行させる条件を反転し、その条件下でループ本体の残りを収集することです。したがって、次のループ

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

書ける

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

それは十分に明確であり、通常、ループ操作を制御する一連の複雑なカリングがない限り、負担にはなりません。

64
RBerteig

Lua 5.2では、gotoを使用するのが最善の回避策です。

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

これは、バージョン2.0.1以降のLuaJITでサポートされています

64
catwell

ループ本体を追加のrepeat until trueでラップしてから、継続の効果のためにdo break endを内部で使用できます。当然のことながら、breakもループから外す場合も、追加のフラグを設定する必要があります。

これは5回ループし、毎回1、2、および3を出力します。

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

この構造は、Luaバイトコードのリテラル1オペコードJMPにも変換されます!

$ 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
38
Oleg V. Volkov

最初の部分は FAQslain が指摘されているように回答されます。

回避策として、ループの本体を関数でラップし、それからreturnを早くすることができます。

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

または、breakcontinueの両方の機能が必要な場合は、ローカル関数にテストを実行させます。

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
finnw

Lua自身のデザイナーによるストレート

「継続」に関する私たちの主な関心事は、(私たちの見解では)「継続」とほぼ同じくらい重要であり、それを置き換えることさえできるいくつかの他の制御構造があることです。 (たとえば、[Javaのように]ラベルでブレークする、またはより一般的なgoto。) "continue"は、より多くの言語に存在することを除いて、他の制御構造メカニズムよりも特別なようには見えません。 (Perlには、実際には「next」と「redo」の2つの「continue」ステートメントがあります。どちらも便利です。)

15

私は以前にLuaを使用したことはありませんでしたが、Googleで検索し、これを思いつきました。

http://www.luafaq.org/

質問1.26 を確認してください。

これはよくある苦情です。 Luaの著者は、continueは多くの新しい制御フローメカニズムの1つに過ぎないと考えていました(繰り返し/終了までのスコープルールで動作できないという事実が二次的な要因でした)。

Lua 5.2には、同じ仕事をするために簡単に使用できるgotoステートメントがあります。

8
slain

このシナリオに何度も遭遇し、単純にフラグを使用して続行をシミュレートしました。 gotoステートメントも使用しないようにします。

例:コードは、i = 3を除くi = 1からi = 10までのステートメントを印刷することを意図しています。さらに、「loop start」、「loop end」、「if start」、および「if end」も出力して、コードに存在する他のネストされたステートメントをシミュレートします。

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

ループの終了スコープまでテストフラグで残りのすべてのステートメントを囲むことによって実現されます。

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

これが最善のアプローチであると言っているわけではありませんが、完全に機能しています。

5
winux

以下のように達成できます。偶数をスキップします

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

ここでも反転を使用すると、次のコードを使用できます。

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