回答:
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でサポートされています。
continue
一日が含まれているといいのですが。goto
交換は非常に素晴らしい見て、より多くの行を必要としません。また、1つの関数でこれを実行する複数のループがあり、両方ともループがある場合、問題は発生し::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
存在する場合、条件で使用されるすべての変数がスコープに入った後でのみ有効になるように、意味的に制限する必要があります。これは、ユーザーに文書化してコンパイラーに適用するのが難しい条件です。ループcontinue
のrepeat ... until
スタイルで許可しないという単純な答えを含め、この問題に関するさまざまな提案が議論されています。これまでのところ、それらを言語に含めるための十分に説得力のあるユースケースはありません。
一般に、回避策は、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
それは十分に明確であり、ループ操作を制御する一連の精巧なカリングがない限り、通常は負担にはなりません。
until...
。
goto
Lua 5.2に導入される前に、Luaコミュニティでこれについて多くの議論がありました。当然、goto
同じ問題があります。彼らは最終的に、実行時間やコード生成のコストがそれから保護することは何でも、goto
両方continue
とマルチレベルの両方をエミュレートするために使用できる柔軟性を持つことの利点に見合うと判断しましたbreak
。詳細を取得するには、関連するスレッドのLuaリストアーカイブを検索する必要があります。彼らが導入したので、goto
明らかに乗り越えられなかったわけではありません。
local
コンパイラのみのディレクティブです-ランタイムの命令local
と変数の使用の間に関係はありません-同じスコープの動作を維持するためにコンパイラで何かを変更する必要はありません。はい、これはそれほど明白ではなく、いくつかの追加のドキュメントが必要な場合がありますが、繰り返しますが、コンパイラでのゼロ変更が必要です。repeat do break end until true
私の答えの例では、コンパイラが続行する場合とまったく同じバイトコードがすでに生成されていcontinue
ますが、唯一の違いは、それを使用するために醜い追加の構文を必要としないことです。
do{int i=0;}while (i == 0);
失敗、またはC ++:do int i=0;while (i==0);
での失敗も検討してください(「このスコープでは宣言されていません」)。残念ながら、今ルアでそれを変えるには遅すぎます。
ループ本体を追加でラップし、継続の効果の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
この構造JMP
は、Luaバイトコードのリテラル1オペコードにも変換されます。
$ 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
luac
、SOで出力を提供するのを見るのはうれしいです!当然の
「継続」に関する私たちの主な懸念は、「継続」とほぼ同じくらい重要であり、それを置き換えることさえあるいくつかの他の制御構造があることです。(たとえば、[Javaの場合のように]ラベルで中断するか、より一般的なgotoです。)「続行」は、より多くの言語で存在することを除いて、他の制御構造メカニズムよりも特別なようには見えません。(Perlには実際には、「次へ」と「やり直し」という2つの「継続」ステートメントがあります。どちらも便利です。)
continue
ルアに入れるのを忘れてしまってすみません」ほど合理的に聞こえませんでした。
殺害が指摘したように、最初の部分はFAQで回答されています。
回避策としては、ループの本体を関数でラップし、それから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
collectgarbage("count")
単純な100回の試行の後も確認してから、話をします。このような「時期尚早」の最適化により、先週、毎分1回の再起動から1つの高負荷プロジェクトを救いました。
これまでLuaを使用したことはありませんが、Googleを使ってこれを思いつきました。
質問1.26を確認してください。
これはよくある不満です。Luaの作成者は、continueは、考えられる多くの新しい制御フローメカニズムの1つにすぎないと感じました(繰り返しのスコープルールでは機能できないという事実が二次的要因でした)。
Lua 5.2には、同じ作業を行うために簡単に使用できるgotoステートメントがあります。
このシナリオに何度も遭遇し、フラグを使用して続行をシミュレートしました。gotoステートメントも使用しないようにしています。
例:このコードは、i = 1からi = 3を除くi = 10までのステートメントを出力することを目的としています。さらに、「ループスタート」、「ループエンド」、「ifスタート」、「ifエンド」も出力して、コード内に存在する他のネストされたステートメントをシミュレートします。
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
これが最善のアプローチであると言っているわけではありませんが、それは私たちにとって完璧に機能します。
Luaは、可能な限り小さくしたい軽量のスクリプト言語です。たとえば、前後のインクリメントなど、多くの単項演算は利用できません。
続行する代わりに、gotoを次のように使用できます
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
この場合も、反転すると、次のコードを使用できます。
for k,v in pairs(t) do
if not isstring(k) then
-- do something to t[k] when k is not a string
end
不要だから¹。開発者がそれを必要とする状況はほとんどありません。
A)非常に単純なループ(1ライナーまたは2ライナーなど)がある場合は、ループ条件を変えるだけで、十分に読みやすくなります。
B)単純な手続き型コード(前世紀のコードの記述方法)を記述している場合は、構造化プログラミング(前世紀のコードの記述方法)も適用する必要があります。
C)オブジェクト指向のコードを記述している場合、1行または2行で表現できない場合を除いて、ループ本体は1つまたは2つのメソッド呼び出しで構成する必要があります(この場合、Aを参照)。
D)関数型コードを記述している場合は、次の反復のために単純な末尾呼び出しを返すだけです。
continue
キーワードを使用する唯一のケースは、PythonのようにLuaをコーディングする場合ですが、Pythonはそうではありません。²
A)が適用されない限り、その場合の回避策は必要ありません。構造化、オブジェクト指向、または関数型プログラミングを実行する必要があります。これらはLuaが構築されたパラダイムです。そのため、パターンを回避するために邪魔をすると、言語と戦うことになります。³
いくつかの明確化:
¹Luaは非常にミニマルな言語です。できる限り少ない機能を使用しようとしますがcontinue
、その意味ではステートメントは必須の機能ではありません。
このミニマリズムの哲学は、この2019年のインタビューでRoberto Ierusalimschyによってよく理解されていると思います。
あれこれあれこれを出して、最後に私たちは最終的な結論がほとんどの人々を満足させないことを理解し、誰もが望むすべてのオプションを置くわけではないので、何も入れません。結局、strictモードは妥当な妥協案です。
²他の言語からLuaに来るプログラマーの数は非常に多いようです。なぜなら、スクリプトを作成しようとしているプログラムがたまたまそれを使用しているからです。多くのプログラマーは、自分の言語以外のものを書きたくないようです。これは、「LuaにX機能がないのはなぜですか?」などの多くの質問につながります。
Matzは最近のインタビューでRubyの同様の状況を説明しました:
最も人気のある質問は、「私は言語Xコミュニティの出身です。言語XからRubyに機能を紹介できませんか?」などです。そして、これらの要求に対する私の通常の答えは...「いいえ、私はそれをしません」です。なぜなら、私たちは異なる言語設計と異なる言語開発ポリシーを持っているからです。
thisこれを回避する方法はいくつかあります。一部のユーザーはgoto
、を使用することを提案しています。これはほとんどの場合十分な近似ですが、非常に醜くなり、ネストされたループで完全に壊れます。goto
また、s を使用すると、コードを他の人に見せるたびにSICPのコピーがスローされる危険があります。
continue
便利な機能かもしれませんが、それはそれを必要としません。多くの人がLuaを使わなくても問題なく使用できます。そのため、プログラミング言語に不可欠ではないきちんとした機能以外にLuaを使用することはできません。
goto
続行の実装に使用できるステートメントを取得しました。以下の回答をご覧ください。