文章目录
- 函数
- 函数嵌套
- 闭包Closures
- 可变函数
- 函数重载
函数
函数嵌套
function A()
print("这里是函数A")
return function ()
print("返回函数不要起名")
end
end
B = A()
B()
输出:
这里是函数A
返回函数不要起名
使用函数嵌套的用法,我们可以将另一个函数作为返回值,但是返回函数作为一个值是要被赋值给其他变量的,所以return时不能起名(赋值)为其他变量名。
闭包Closures
推荐阅读深入Lua:函数和闭包
在函数嵌套中,我们需要接触一个叫做闭包的概念
这就是一个闭包,它由一个函数和该函数会访问到的非局部变量(或者说upvalue)组成
function f1(n)
--函数参数n也是局部变量
local function f2()
print(n) --引用外部函数的局部变量,即upvalue
end
return f2
end
a =f1(1) --注意,闭包不传入参数则为nil
a()
当一个函数内嵌套另一个函数的时候,内函数可以访问外部函数的局部变量,这种特征被称为词法域,什么意思呢?
例如上述f1的入参n,它是一个形参,也是f1内的局部变量。现在f1内部定义了另一个函数f2,显然f2需要访问f1内的局部变量 n。但是对于n而言,它的词法域或者说作用域只是在f1内。如果只是简单的函数嵌套的话,用其他语言可以是这样实现的:
void f1(int x){
f2(x)
}
void f2(int x){
return x
}
但是问题是上述定义中f1内的x和f2内的x并不是同一个x,而分别是它们作用域内定义的局部变量。
而闭包更相当于一个指针,使得f2直接引用了f1的局部变量
#include <iostream>
using namespace std;
void f2(int *x)
{
printf("%d", *x);
}
void f1(int x)
{
f2(&x);
}
int main()
{
int i = 1;
f1(i);
return 0;
}
这就是闭包的概念:内部函数innerFunction能够访问并持有其外部函数outerFunction作用域中的变量,这些被内部函数引用的外部函数的局部变量被称为upvalues。实际上,一般的函数在lua中是一种特殊的闭包。整个函数产生的闭包类似于下列struct :
// Lua闭包
typedef struct LClosure {
ClosureHeader;
struct Proto *p; // 函数原型
UpVal *upvals[1]; /* list of upvalues */ // upvalue列表
} LClosure;
function Create(n)
local function f1()
print(n)
end
local function f2()
n = n + 10
end
return f1,f2
end
a,b = Create(10)
a() -- 10
b()
a() -- 20
b()
a() -- 30
在上述闭包中,两个闭包f1,f2使用的n是同一个局部变量,因此f2使n增加后,f1输出值也变了。它们共享一个upvalues。
现在假设要创建一个对象,对外只提供有限的访问接口,而对象内部的数据不能直接被修改,那么我们可以这样写:
local function new_object()
local obj = { -- 这就是要创建的对象
_data1 = 1, -- 假设这是内部数据
_data2 = 2, -- 这是外部可修改的数据
}
return { -- 这是返回的接口table
get_data2 = function() return obj._data2 end,
set_data2 = function(value) obj._data2 = value end,
}
end
local obj_inteface = new_object()
obj_inteface.set_data2(100)
print(obj_inteface.get_data2()) --> 100
可变函数
function f1( x,...)
arg={...}
for i=1,#arg do
print(arg[i])
end
end
我们用…表示参数是可变参数,它能接收任意长的参数,但是在我们使用的时候,最好使用一个table来接收这个可变参数。此外其他固定的入参一定要放在可变参数的前面,这样函数才能先接受入参,其他的丢给可变参数。
函数重载
function A()
print(123)
end
A()
function A(a)
print(a)
end
A(1)
输出:
123
1
在Lua中,函数的重载十分简单,只需在函数下方重写这个函数即可。在Unity中Lua能实现热更新,例如修正一些Bug,就是通过重载函数实现的,举个例子:
摘自Lua语言:函数级别的重载
-- hotfix.lua
--- 执行热更新
-- oldmod是旧模块
-- newmod是新模块,这个模块里只会提供要替换的函数,相当于旧模块的一个子集。
function hotfix.run(oldmod, newmod)
-- 收集旧模块的所有upvalue
local uvmap = collect_all_upvalue(oldmod)
for k, v in pairs(newmod) do
if type(v) == 'function' then
-- 这里就是先把新函数的upvalue修正,然后直接替换给旧模块
oldmod[k] = hotfix_func(v, uvmap)
end
end
end
return hotfix