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

fro*_*oyo 31 variables lua scope

我完全被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) 
Run Code Online (Sandbox Code Playgroud)

因此,这种行为完全让我感到困惑.

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

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

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

Bas*_*ink 44

你猜对了,表变量是通过引用传递的.引用Lua 5.1参考手册:

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

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

所以零,布尔值,数字和字符串都按值传递.这准确地解释了您观察到的行为.

  • 这与传递参考略有不同.(见我的回答).特别是`function(x)x = {} end`的行为是不同的. (4认同)
  • 一切都是按值传递,某些类型(表,函数,线程和(完整)用户数据值)是引用.这些引用按值传递. (4认同)

jA_*_*cOp 20

Lua的function,table,userdatathread(协程)类型按引用传递.其他类型按值传递.或者像有些人喜欢说的那样; 所有类型按值传递,但是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
Run Code Online (Sandbox Code Playgroud)

也许这会让事情成为可能,为什么事情就是这样:

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
Run Code Online (Sandbox Code Playgroud)


Mic*_*son 19

当jA_cOp说"所有类型都是按值传递,但函数,表,用户数据和线程是引用类型"时,它是正确的."

这个和"表通过引用传递"之间的区别很重要.

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

function modt_1(x)
  x.foo = "bar"
end
Run Code Online (Sandbox Code Playgroud)

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

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

function modt_2(x)
  x = {}
end
Run Code Online (Sandbox Code Playgroud)

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


Zec*_*ecc 7

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

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

这是不正确的.Lua中的变量是词法范围的,这意味着它们是在代码块及其所有嵌套块中定义的.
什么local是创建一个新的变量,它仅限于语句所在的块,一个块是一个函数体,一个"缩进级别"或一个文件.

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

在这种情况下,a并且t正在被声明为本地但声明属于全球范围,因此a并且t是全球性的; 或者最多,它们是当前文件的本地文件.

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

在lua-users.org上有一个范围教程,其中有一些示例可能比我尝试解释更有帮助.Lua关于这个主题的部分的编程也很好.