私は過去数ヶ月間、Luaをたくさん扱ってきました。私はほとんどの機能が本当に好きですが、それらの中でまだ何かが欠けています:
continue
がないのはなぜですか?言語がレキシカルスコープを管理する方法は、goto
とcontinue
の両方を含めることで問題を引き起こします。例えば、
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
それは十分に明確であり、通常、ループ操作を制御する一連の複雑なカリングがない限り、負担にはなりません。
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でサポートされています
ループ本体を追加の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
最初の部分は FAQ で slain が指摘されているように回答されます。
回避策として、ループの本体を関数でラップし、それから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
または、break
とcontinue
の両方の機能が必要な場合は、ローカル関数にテストを実行させます。
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
「継続」に関する私たちの主な関心事は、(私たちの見解では)「継続」とほぼ同じくらい重要であり、それを置き換えることさえできるいくつかの他の制御構造があることです。 (たとえば、[Javaのように]ラベルでブレークする、またはより一般的なgoto。) "continue"は、より多くの言語に存在することを除いて、他の制御構造メカニズムよりも特別なようには見えません。 (Perlには、実際には「next」と「redo」の2つの「continue」ステートメントがあります。どちらも便利です。)
私は以前にLuaを使用したことはありませんでしたが、Googleで検索し、これを思いつきました。
質問1.26 を確認してください。
これはよくある苦情です。 Luaの著者は、continueは多くの新しい制御フローメカニズムの1つに過ぎないと考えていました(繰り返し/終了までのスコープルールで動作できないという事実が二次的な要因でした)。
Lua 5.2には、同じ仕事をするために簡単に使用できるgotoステートメントがあります。
このシナリオに何度も遭遇し、単純にフラグを使用して続行をシミュレートしました。 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
これが最善のアプローチであると言っているわけではありませんが、完全に機能しています。
以下のように達成できます。偶数をスキップします
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
ここでも反転を使用すると、次のコードを使用できます。
for k,v in pairs(t) do
if not isstring(k) then
-- do something to t[k] when k is not a string
end