函数/变量范围(通过值或引用传递?)
Posted
技术标签:
【中文标题】函数/变量范围(通过值或引用传递?)【英文标题】:Function/variable scope (pass by value or reference?) 【发布时间】:2011-09-01 22:59:43 【问题描述】:我完全被 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
无法
修改 a 变量?
【问题讨论】:
【参考方案1】:你猜对了,表变量是通过引用传递的。引用Lua 5.1 Reference Manual:
Lua 中有八种基本类型:nil、boolean、number、string、function、userdata、thread 和 table。 ....
表、函数、线程和(完整的)用户数据值都是对象:变量实际上并不包含这些值,只是对它们的引用。赋值、参数传递和函数返回总是操纵对这些值的引用;这些操作并不意味着任何形式的复制。
所以 nil、布尔值、数字和字符串都是按值传递的。这正好解释了您观察到的行为。
【讨论】:
这与传递引用略有不同。 (见我的回答)。特别是function(x) x= end
的行为是不同的。
一切都是按值传递的,按某些类型(表、函数、线程和(完整)用户数据值)是引用。这些引用是按值传递的。【参考方案2】:
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
【讨论】:
【参考方案3】:jA_cOp 说“所有类型都是按值传递,但函数、表、用户数据和线程都是引用类型”是正确的。
这与“表通过引用传递”之间的区别很重要。
在这种情况下没有区别,
function modt_1(x)
x.foo = "bar"
end
结果:“按引用传递表”和“按值传递表,但表是引用类型”都将执行相同的操作:x 现在将其 foo 字段设置为“bar”。
但是对于这个功能,它会产生很大的不同
function modt_2(x)
x =
end
在这种情况下,通过引用传递将导致参数更改为空表。但是在“按值传递,但它是引用类型”中,一个新的表将在本地绑定到 x,并且参数将保持不变。如果你在 lua 中尝试这个,你会发现它是第二个发生的(值是引用)。
【讨论】:
我找到了一种很好的思考方式,即一切都是按值传递的。然而,有些类型只是引用而已。引用本身是按值传递的,这就是您的示例不更改“t”的原因。很好的解释:)【参考方案4】:我不会重复在 Bas Bossink 和 jA_cOp 关于引用类型的回答中已经说过的话,但是:
-- 因为它是本地定义的,所以不应该有 func 范围
这是不正确的。 Lua 中的变量是lexically scoped,这意味着它们定义在一个代码块及其所有嵌套块中。local
所做的是创建一个新变量,该变量仅限于语句所在的块,一个块可以是函数体、“缩进级别”或文件。
这意味着每当你引用一个变量时,Lua 都会“向上扫描”,直到它找到一个代码块,其中该变量被声明为本地的,如果没有这样的声明,则默认为全局范围。
在这种情况下,a
和 t
被声明为本地但声明是在全局范围内,所以 a
和 t
是全局的;或者最多,它们是当前文件的本地文件。
它们不会在函数内部重新声明local
,但是它们被声明为参数,具有相同的效果。如果它们不是函数参数,那么函数体内的任何引用仍然会引用外部的变量。
在 lua-users.org 上有一个 Scope Tutorial,其中的一些示例可能比我试图解释的方式更能帮助您。 Programming in Lua's section on the subject 也是一本好书。
【讨论】:
【参考方案5】:这是否意味着表变量是通过引用而不是值传递给函数的?
是的。
全局变量创建与已经定义的局部变量如何冲突?
不是。可能会出现这种情况,因为您有一个名为 t
的全局变量并将其传递给带有名为 t
的参数的函数,但两个 t
s 是不同的。如果您将参数重命名为其他名称,例如q
,则输出将完全相同。 modt(t)
能够修改全局变量 t
只是因为您通过引用传递它。例如,如果您调用modt()
,则全局t
将不受影响。
为什么 modt 可以修改表而 moda 不能修改 a 变量?
因为参数是本地的。将参数命名为 a
类似于使用 local a
声明局部变量,但显然参数接收传入的值而常规局部变量没有。如果您的论点被称为z
(或根本不存在),那么moda
确实会修改全局a
。
【讨论】:
以上是关于函数/变量范围(通过值或引用传递?)的主要内容,如果未能解决你的问题,请参考以下文章