函数定义的语法如下:
function ::= function funcbody funcbody ::= `(´ [parlist1] `)´ block end
另外定义了一些语法糖简化函数定义的写法:
stat ::= function funcname funcbody stat ::= local function Name funcbody funcname ::= Name {`.´ Name} [`:´ Name]
这样的写法:
function f () body end
被转换成
f = function () body end
这样的写法:
function t.a.b.c.f () body end
被转换成
t.a.b.c.f = function () body end
这样的写法:
local function f () body end
被转换成
local f; f = function () body end
注意,并不是转换成
local f = function () body end
(这个差别只在函数体内需要引用 f
时才有。)
一个函数定义是一个可执行的表达式, 执行结果是一个类型为 function 的值。 当 Lua 预编译一个 chunk 的时候, chunk 作为一个函数,整个函数体也就被预编译了。 那么,无论何时 Lua 执行了函数定义, 这个函数本身就被实例化了(或者说是关闭了)。 这个函数的实例(或者说是 closure(闭包)) 是表达式的最终值。 相同函数的不同实例有可能引用不同的外部局部变量, 也可能拥有不同的环境表。
形参(函数定义需要的参数)是一些由实参(实际传入参数)的值初始化的局部变量:
parlist1 ::= namelist [`,´ `...´] | `...´
当一个函数被调用,
如果函数没有被定义为接收不定长参数,即在形参列表的末尾注明三个点 ('...
'),
那么实参列表就会被调整到形参列表的长度,
变长参数函数不会调整实参列表;
取而代之的是,它将把所有额外的参数放在一起通过变长参数表达式传递给函数,
其写法依旧是三个点。
这个表达式的值是一串实参值的列表,看起来就跟一个可以返回多个结果的函数一样。
如果一个变长参数表达式放在另一个表达式中使用,或是放在另一串表达式的中间,
那么它的返回值就会被调整为单个值。
若这个表达式放在了一系列表达式的最后一个,就不会做调整了(除非用括号给括了起来)。
我们先做如下定义,然后再来看一个例子:
function f(a, b) end function g(a, b, ...) end function r() return 1,2,3 end
下面看看实参到形参数以及可变长参数的映射关系:
CALL PARAMETERS f(3) a=3, b=nil f(3, 4) a=3, b=4 f(3, 4, 5) a=3, b=4 f(r(), 10) a=1, b=10 f(r()) a=1, b=2 g(3) a=3, b=nil, ... --> (nothing) g(3, 4) a=3, b=4, ... --> (nothing) g(3, 4, 5, 8) a=3, b=4, ... --> 5 8 g(5, r()) a=5, b=1, ... --> 2 3
结果由 return 来返回(参见 §2.4.4)。 如果执行到函数末尾依旧没有遇到任何 return 语句, 函数就不会返回任何结果。
冒号语法可以用来定义方法,
就是说,函数可以有一个隐式的形参 self
。
因此,如下写法:
function t.a.b.c:f (params) body end
是这样一种写法的语法糖:
t.a.b.c.f = function (self, params) body end