Luaの変数スコープと関数引数の受け渡し(値または参照)に完全に混乱しています。
以下のコードを参照してください。
local a = 9 -- since it's define local, should not have func scope
local t = {4,6} -- since it's define local, should not have func scope
function moda(a)
a = 10 -- creates a global var?
end
function modt(t)
t[1] = 7 -- create a global var?
t[2] = 8
end
moda(a)
modt(t)
print(a) -- print 9 (function does not modify the parent variable)
print(t[1]..t[2]) -- print 78 (some how modt is modifying the parent t var)
そのため、この動作は私を完全に混乱させます。
これは、テーブル変数が値ではなく参照によって関数に渡されることを意味しますか?
グローバル変数の作成は、すでに定義されているローカル変数とどのように競合しますか?
modt
はテーブルを変更できるのに、moda
は変数を変更できないのですか?ご想像のとおり、テーブル変数は参照渡しされます。引用 Lua 5.1リファレンスマニュアル :
Luaには、nil、boolean、number、string、function、userdata、thread、およびtableの8つの基本タイプがあります。 ....
テーブル、関数、スレッド、および(完全な)ユーザーデータ値はオブジェクトです。変数には実際にはこれらの値は含まれず、それらへの参照のみが含まれます。割り当て、パラメーターの受け渡し、および関数の戻り値は、常にそのような値への参照を操作します。これらの操作は、いかなる種類のコピーも意味しません。
したがって、nil、boolean、number、stringは値で渡されます。これは、観察する動作を正確に説明しています。
Luaのfunction
、table
、userdata
およびthread
(コルーチン)型は参照によって渡されます。他のタイプは値で渡されます。または、一部の人々がそれを好むように。すべての型は値で渡されますが、function
、table
、userdata
、およびthread
は参照型です。
string
も一種の参照型ですが、不変、インターン、およびコピーオンライトです。値型のように動作しますが、パフォーマンスは向上します。
ここで何が起こっているのですか:
local a = 9
local t = {4,6}
function moda(a)
a = 10 -- sets 'a', which is a local introduced in the parameter list
end
function modt(t)
t[1] = 7 -- modifies the table referred to by the local 't' introduced in the parameter list
t[2] = 8
end
おそらく、これにより、物事がなぜそうであるのかについて、物事を見通しに入れることができます:
local a = 9
local t = {4,6}
function moda()
a = 10 -- modifies the upvalue 'a'
end
function modt()
t[1] = 7 -- modifies the table referred to by the upvalue 't'
t[2] = 8
end
-- 'moda' and 'modt' are closures already containing 'a' and 't',
-- so we don't have to pass any parameters to modify those variables
moda()
modt()
print(a) -- now print 10
print(t[1]..t[2]) -- still print 78
「すべての型は値によって渡されますが、関数、テーブル、ユーザーデータ、およびスレッドは参照型です」と言うとき、jA_cOpは正しいです。
これと「テーブルは参照渡し」の違いは重要です。
この場合、違いはありませんが、
function modt_1(x)
x.foo = "bar"
end
結果:「テーブルを参照で渡す」と「テーブルを値で渡すが、テーブルは参照型である」の両方で同じことが行われます。xのfooフィールドは「bar」に設定されます。
しかし、この機能のために、それは違いの世界を作ります
function modt_2(x)
x = {}
end
この場合、参照渡しでは、引数が空のテーブルに変更されます。ただし、「値による受け渡しですが、参照型」では、新しいテーブルはローカルにxにバインドされ、引数は変更されません。 luaでこれを試してみると、2番目(値は参照)であることがわかります。
Bas Bossinkと参照タイプに関するjA_cOpの回答で既に述べたことを繰り返しませんが、
-ローカル定義であるため、funcスコープを使用しないでください
これは間違っています。 Luaの変数は lexically scoped です。つまり、コードのブロックとそのすべてのネストされたブロックで定義されます。local
が行うことは、ステートメントが存在するブロック、関数の本体、「インデントのレベル」、またはファイルのいずれかに制限される新しい変数を作成することです。
つまり、変数を参照するたびに、Luaはその変数がローカルで宣言されているコードブロックが見つかるまで「上方向にスキャン」し、そのような宣言がない場合はデフォルトでグローバルスコープになります。
この場合、a
とt
はローカルで宣言されていますが、宣言はグローバルスコープ内にあるため、a
とt
はグローバルです。または最大で、現在のファイルに対してローカルです。
これらは関数内でlocal
で再宣言されません、しかしパラメーターとして宣言され、同じ効果があります。それらが関数パラメーターでない場合、関数本体内部の参照は外部の変数を参照します。
Lua-users.orgには Scopeチュートリアル があり、説明での私の試み以上に役立ついくつかの例があります。 主題に関するLuaのセクションでのプログラミング も良い読み物です。
これは、テーブル変数が値ではなく参照によって関数に渡されることを意味しますか?
はい。
グローバル変数の作成は、すでに定義されているローカル変数とどのように競合しますか?
そうではありません。 t
というグローバル変数があり、t
という引数を持つ関数に渡すため、2つのt
sが異なるため、そのように見えるかもしれません。引数の名前をq
などの別の名前に変更すると、出力はまったく同じになります。 modt(t)
は、グローバル変数t
を変更できます。これは、参照で渡すためです。たとえば、modt({})
を呼び出す場合、グローバルt
は影響を受けません。
Modtがテーブルを変更できるのに、modaは変数を変更できないのはなぜですか?
引数はローカルだからです。引数の名前付けa
は、引数が渡された値を受け取り、通常のローカル変数が受け取らないことを除いて、local a
でローカル変数を宣言するのに似ています。引数がz
と呼ばれた(またはまったく存在しなかった)場合、moda
は実際にグローバルa
を変更します。