2011-05-25 56 views
29

我完全困惑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不能 修改一个变量?

回答

37

你猜对了,表变量是通过引用传递。引用Lua 5.1 Reference Manual

在Lua中有八种基本类型:nil,boolean,number,string,function,userdata,thread和table。 ....

表,函数,线程和(完整)用户数据值是对象:变量实际上不包含这些值,只包含对它们的引用。赋值,参数传递和函数返回总是处理对这些值的引用;这些操作并不意味着任何形式的副本。

因此零,布尔值,数字和字符串都是按值传递的。这正好解释了你观察到的行为。

+4

这与通过引用略有不同。 (看我的答案)。特别是'function(x)x = {} end'的行为是不同的。 – 2011-12-02 08:32:03

+1

所有东西都是按值传递的,通过某些类型(表,函数,线程和(完整)用户数据值)是引用。这些引用是按值传递的。 – Ethan 2015-10-30 01:10:16

18

lua的functiontableuserdatathread(协程)类型通过引用传递。其他类型通过值传递。或者像有些人喜欢说的那样;所有类型都按值传递,但function,table,userdatathread是引用类型。

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 
7

我不会重复已经在巴斯Bossink和jA_cOp的关于引用类型的回答说,但是:

- 因为它定义的地方,不应该有FUNC范围

这是不正确的。 Lua中的变量为lexically scoped,这意味着它们是在一段代码及其所有嵌套块中定义的。
local所做的是创建一个新变量,该变量仅限于语句所在的块,块可以是函数的主体,“缩进级别”或文件。

这意味着只要您对变量进行引用,Lua就会“向上扫描”,直到找到一个代码块,其中该变量被声明为本地代码块,如果没有此类声明,则默认为全局代码块。

在这种情况下,at被声明的局部,但声明在全球范围内,所以at是全球性的;或者至多,它们是当前文件的本地文件。

他们然后不被重新声明local里面的功能,他们声明为参数,它具有相同的效果。如果它们不是函数参数,函数体内的任何引用仍然会引用外部变量。

lua-users.org上有一个Scope Tutorial,其中一些例子可能比我的解释更有帮助。 Programming in Lua's section on the subject也是一个很好的阅读。

1

这是否意味着表变量通过引用传递给函数而不是值?

是的。

全局变量创建如何与已定义的局部变量冲突?

不是。这可能会出现这种情况,因为你有一个叫做t的全局变量,并将它传递给一个名为t的参数,但这两个t是不同的。如果您将参数重命名为其他东西,e,g,q,则输出将完全相同。 modt(t)只能因为通过引用传递它而能够修改全局变量t。例如,如果您拨打modt({}),全球t将不受影响。

为什么模式能够修改表格但moda无法修改变量?

因为参数是本地的。命名参数a类似于声明一个局部变量local a,除非显然该参数接收传入的值,而常规的局部变量则不会。如果您的论点被称为z(或根本不存在),那么moda确实会修改全球a

16

jA_cOp是正确的,当他说“所有类型都通过值传递,但函数,表,用户数据和线程都是引用类型”。

这和“表格通过引用传递”之间的区别很重要。

在这种情况下它没有什么区别,

function modt_1(x) 
    x.foo = "bar" 
end 

结果:两个“按引用传递表”和“按值传递表,但表是引用类型”将这样做:X现在有它的foo字段设置为“bar”。

但这个功能它使差异

function modt_2(x) 
    x = {} 
end 

的世界。在这种情况下,通过引用传递将导致参数得到改变为空表。但是,在“按值传递,但是它是一个引用类型”的情况下,新表将局部绑定到x,并且参数将保持不变。如果你在lua中试试这个,你会发现它是第二个(值是引用)。

+0

这真的很有帮助,谢谢 – 2014-12-14 17:07:54

+0

我发现一个很好的方式来思考这一切,只是一切都是按价值传递的。然而,恰巧有些类型只是参考。引用本身是通过值传递的,这就是为什么你的例子不会改变't'。很好的解释 :) – Ethan 2015-10-30 01:09:18