函数/变量作用域(传值还是传引用?)
我对**Lua**的变量作用域和函数参数传递(值传递还是引用传递)完全困惑了。
请看下面的代码:
local a = 9 -- 由于是定义为局部变量,不应该具有函数作用域
local t = {4,6} -- 由于是定义为局部变量,不应该具有函数作用域
function moda(a)
a = 10 -- 创建了一个全局变量?
end
function modt(t)
t[1] = 7 -- 创建了一个全局变量?
t[2] = 8
end
moda(a)
modt(t)
print(a) -- 输出9(函数没有修改父变量)
print(t[1]..t[2]) -- 输出78(某种方式下modt正在修改父t变量)
因此,这种行为完全让我困惑。
这是否意味着表变量通过引用而不是值传递传递给函数?
全局变量的创建如何与已经定义的局部变量冲突?
- 为什么
modt
能够修改表,而moda
无法更改变量a?
- 为什么
原文链接 https://stackoverflow.com/questions/6128152
你猜对了,表变量是通过引用传递的。引用 Lua 5.1参考手册 的说明:
Lua 有八种基本类型: nil, boolean, number, string, function, userdata, thread, and table. ……
表、函数、线程和(完整的)用户数据值是对象: 变量实际上不包含这些值,只包含对它们的引用。赋值、参数传递和函数返回总是操作对这些值的引用; 这些操作不意味着任何类型的副本。
因此,nil、布尔值、数字和字符串是按值传递的。这完全解释了您观察到的行为。
我不会重复 Bas Bossink 和 jA_cOp 关于引用类型的答案,但是:
-- 因为它是定义本地的,所以不应该具有函数作用域
这是错误的。在 Lua 中,变量是 词法作用域,这意味着它们被定义在一段代码块及其嵌套的所有块中。
local
所做的是创建一个新的变量,该变量仅限于语句所在的块,块可以是一个函数的主体,一个“缩进级别”或一个文件。
这意味着每当你引用一个变量时,Lua 将“向上扫描”,直到找到一个代码块,在该代码块中声明了该变量为本地变量,如果没有这样的声明,则默认为全局作用域。
在这种情况下,a
和 t
被声明为本地变量,但声明在全局范围内,因此 a
和 t
是全局的;或者至多是在当前文件中本地的。
然后它们在函数中没有被重新声明为 local
,但它们被声明为参数,这具有相同的效果。如果它们不是函数参数,函数体中的任何引用仍将指向外部的变量。
在 lua-users.org 上有一个 作用域教程,其中有一些示例,可能比我尝试的解释更有帮助。Programming in Lua 中的这个主题也是一个不错的阅读材料。
这是否意味着表变量是按引用而不是按值传递给函数的?
是的。
全局变量的创建与已经定义的局部变量有什么冲突?
没有。这可能是因为你有一个名为 t
的全局变量,并将其传递给一个名为 t
的参数的函数,但这两个 t
是不同的。如果将参数重命名为其他名称,例如 q
,输出将完全相同。modt(t)
能够修改全局变量t
,仅因为您通过引用传递它。如果您调用modt({})
,全局t
将不受影响。
为什么modt能够修改表,而moda不能修改变量a?
因为参数是局部的。将参数命名为a
类似于使用local a
声明局部变量,除了显然该参数接收传入值,而常规局部变量不会。如果你的参数被叫做z
(或者根本不存在),那么moda
确实会修改全局变量a
。
jA_cOp 在他所说的 "所有类型都是按值传递的,但是函数、表、用户数据和线程是引用类型" 的观点上是正确的。
这与 "表是按引用传递的" 的说法有着重要的区别。
在这种情况下没有什么区别,
function modt_1(x)
x.foo = "bar"
end
结果:无论是 "按引用传递表" 还是 "按值传递表,但表是引用类型" 都会导致相同的结果:x 现在具有其 foo 字段的值为 "bar"。
但是对于这个函数这就有了天壤之别
function modt_2(x)
x = {}
end
在这种情况下,按引用传递将导致参数被更改为空表。然而在 "按值传递,但它是引用类型" 中,一个新表将被绑定到 x 中,而参数本身不会被改变。如果你在 lua 中尝试这个操作,你会发现是第二个(值是引用)发生了。
- 如何在roblox studio中1:1导入真实世界的地形?
- 求解,lua_resume的第二次调用继续执行协程问题。
- 【上海普陀区】内向猫网络招募【Skynet游戏框架Lua后端程序员】
- SF爱好求教:如何用lua实现游戏内调用数据库函数实现账号密码注册?
- Lua实现网站后台开发
- LUA错误显式返回,社区常见的规约是怎么样的
- lua5.3下载库失败
- 请问如何实现文本框内容和某个网页搜索框内容连接,并把网页输出来的结果反馈到另外一个文本框上
- lua lanes多线程使用
- 一个kv数据库
- openresty 有没有比较轻量的 docker 镜像
- 想问一下,有大佬用过luacurl吗
- 在Lua执行过程中使用Load函数出现问题
- 为什么 neovim 里没有显示一些特殊字符?
- Lua比较两个表的值(不考虑键的顺序)
- 有个lua简单的项目,外包,有意者加微信 liuheng600456详谈,最好在成都
- 如何在 Visual Studio 2022 中运行 Lua 代码?
- addEventListener 返回 nil Lua
- Lua中获取用户配置主目录的跨平台方法
- 如何编写 Lua 模式将字符串(嵌套数组)转换为真正的数组?
Lua 的
function
、table
、userdata
和thread
(协程)类型是按引用传递的。其他类型则是按值传递。或者像一些人所说的那样:所有类型都是按值传递的,但function
、table
、userdata
和thread
是引用类型。string
也是一种引用类型,但是是不可变的,经过内部化(interned)和写时复制(copy-on-write),它的行为类似于值类型,但具有更好的性能。以下是正在发生的事情:
local a = 9 local t = {4,6} function moda(a) a = 10 -- 设置 'a',它是在参数列表中引入的本地变量 end function modt(t) t[1] = 7 -- 修改由参数列表中引入的本地变量 't' 引用的表 t[2] = 8 end
也许这会让事情变得更清晰:
local a = 9 local t = {4,6} function moda() a = 10 -- 修改闭包 'a' end function modt() t[1] = 7 -- 修改闭包 't' 引用的表 t[2] = 8 end -- 'moda' 和 'modt' 已经包含了 'a' 和 't', -- 所以我们不必传递任何参数来修改那些变量 moda() modt() print(a) -- 输出 10 print(t[1]..t[2]) -- 仍然输出 78