函数/变量范围(按值或引用传递?)

时间:2011-05-25 17:03:10

标签: variables scope lua

我对 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无法修改 修改变量?

5 个答案:

答案 0 :(得分:42)

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

  

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

     

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

所以nil,booleans,数字和字符串按值传递。这准确地解释了您观察到的行为。

答案 1 :(得分:20)

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

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

答案 2 :(得分:19)

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

这与“通过引用传递表格”之间的区别非常重要。

在这种情况下没有区别,

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

结果:“按引用传递表”和“按值传递表,但表是引用类型”将执行相同操作:x现在将其foo字段设置为“bar”。

但是对于这个功能,它创造了一个与众不同的世界

function modt_2(x)
  x = {}
end

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

答案 3 :(得分:7)

我不会重复Bas Bossink和jA_cOp关于参考类型的回答,但是:

  

- 因为它定义了local,所以不应该有func范围

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

这意味着无论何时引用变量,Lua都会“向上扫描”,直到找到一个代码块,其中该变量被声明为local,如果没有这样的声明,则默认为全局范围。

在这种情况下,at被声明为本地,但声明属于全局范围,因此at是全局的;或者至多,它们是当前文件的本地文件。

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

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

答案 4 :(得分:1)

  

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

  

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

不是。它可能看起来是这样的,因为你有一个名为t的全局变量,并将其传递给一个名为t的参数的函数,但两个t是不同的。如果将参数重命名为其他参数,例如,q输出将完全相同。 modt(t)只能修改全局变量t,因为您通过引用传递它。例如,如果您拨打modt({}),则全局t将不受影响。

  

为什么modt能够修改表,但moda无法修改变量?

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