optional_function_scope function function_name( argument1, argument2, argument3..., argumentn)
function_body
return result_params_comma_separated
end
入参的行为
print("参数局部变量:")
function params(a, b)
print("函数内部,未改变值:", a, b) --> 函数内部,未改变值: 1 2
a = 10
b = 100
print("函数内部,未改变值:", a, b) --> 函数内部,未改变值: 10 100
end
local a = 1
local b = 2
print("入参前:", a, b) --> 入参前: 1 2
params(a, b)
print("入参后:", a, b) --> 入参后: 1 2
函数的调用和 java 、 kotlin 没有区别
只是如果函数只有一个参数,且该参数类型是 字符串常量 或是 表构造器 ,则括号是可选,只需隔开一个空格。
-- 等同于 print("function")
print "function" --> function
-- 等同于 dofile("字符串.lua")
dofile "字符串.lua"
-- 等同于 type({})
print(type {}) --> table
参数个数没有规定一定要和函数声明的个数一致,多余会被抛弃,不足参数会被设置为 nil .
function multiParams(p1, p2, p3, p4, p5)
print(p1, p2, p3, p4, p5)
end
multiParams() --> nil nil nil nil nil
multiParams(1) --> 1 nil nil nil nil
multiParams(1, 2) --> 1 2 nil nil nil
multiParams(1, "jiang", 2) --> 1 jiang 2 nil nil
multiParams(1, "jiang", 2, "peng") --> 1 jiang 2 peng nil
multiParams(1, "jiang", 2, "peng", 3) --> 1 jiang 2 peng 3
multiParams(1, "jiang", 2, "peng", 3, "yong") --> 1 jiang 2 peng 3
可以利用这一特性,加上 a or 默认值
达到默认值
local globalCounter = 0
function initGlobal(n)
n = n or 1
globalCounter = globalCounter + n;
end
initGlobal()
print(globalCounter) --> 1
initGlobal(10086)
print(globalCounter) --> 10087
使用 ...
进行接收可变长参数函数,用 {} 装载则将其转为列表,值得注意的是,这里每次调用都会创建一个临时的表
和 kotlin 一样,
_
可以避免无用参数取名
function add(...)
local total = 0
for _, v in ipairs { ... } do
total = total + v;
end
return total
end
print(add(3, 4, 10, 25, 12)) --> 54
-- 如果存在 nil , 则后面的就不再继续,因为使用的是 ipairs , 使用 paris 就不会
print(add(3, 4, 10, nil, 25, 12)) --> 17
可以将 ...
当作多值返回(见下一小节)来使用,直接将其赋值给变量
-- ... 和多值返回类似,可以用多值返回的所有操作
-- 多的会被舍弃,少的用 nil 补充
function foo(...)
local a, b, c = ...
print(a, b, c, "size: " .. #{ ... })
end
foo("a") --> a nil nil size: 1
foo("a", "b") --> a b nil size: 2
foo("a", "b", "c") --> a b c size: 3
foo("a", "b", "c", "d") --> a b c size: 4
foo("a", "b", nil, "c", "d", nil) --> a b nil size: 5 -- 这里的最后一个元素 `nil` 则会被忽视,如果需要被计算则需要用 `table.pack()`
nil 也会被认为是元素
当第一个参数 selector 是数值时,表示从哪个下标开始截取。(开始下标是 1 )
当第一个参数是 # 时,则表示获取长度( nil 也会被包括在内)。
print(select(1, "a", "b", "c")) --> a b c
print(select(1, "a", "b", nil, "c")) --> a b nil c
print(select(2, "a", "b", "c")) --> b c
print(select(3, "a", "b", "c")) --> c
print(select(4, "a", "b", "c")) --> “空”
print(select("#", "a", "b", "c")) --> 3
-- nil 也会被计算
print(select("#", "a", "b", nil, "c", nil)) --> 5
可以使用 select 进行遍历处理
function add1(...)
local s = 0
for i = 1, select("#", ...) do
-- 此处的 select 只会使用 i 下标的值
s = s + select(i, ...)
end
return s
end
print(add1(1, 2, 3, 4, 5))
两种都可以进行遍历元素。
在元素比较少的情况下, select 会快一些,可以避免每次都创建临时新表
元素较多的情况下,则 { … } 会快一些
可以进行多值赋值,多的参数会被抛弃,少的用 nil 补充
对于所有的函数入参都可以使用可变长参数接收,基于这一特性,我们可以使用以下函数进行跟踪一些入参,调试的时候很有用
function showParams(fun, ...)
print(...)
return fun(...)
end
-- 使用
print(showParams(add, 1, 2, 3, 4, 5))
--> 1 2 3 4 5
--> 15
可变参数可以和固定参数一起使用,则像 kotlin 、 java 一样,需要将固定参数放在函数参数的最前面。
函数可以在 return 中返回多个值
遵循几个规则:
可以使用增加括号,让多值返回变为只有一个值返回。
举亿个例子
function f0()
return
end
function f1()
return "jiang"
end
function f2()
return "peng yong", 28
end
获取多值返回
-- 多余的返回值会被舍弃
value = f2()
print(value) --> a
-- 按顺序进行赋值,多余的返回值会被舍弃,少的则会用 nil
value1, value2, value3 = 10, f2()
print(value1, value2, value3) --> 10 a 1
value4, value5, value6, value7 = 10, f2()
print(value4, value5, value6, value7) --> 10 a 1 nil
-- 如果多值返回不是最后一个表达式,则只会使用一个,这个原理同样适用于函数调用入参
value8, value9, value10 = f2(), 10
print(value8, value9, value10) --> a 10 nil
---- f0 返回 nil 赋值给 value11 , 而 10001 多了一个参数则被舍弃
value11, value12 = f0(), 10000, 10001
print(value11, value12) --> nil 10000
将多值返回当作入参
function params(param1, param2, param3)
print("params:" .. param1 .. "," .. param2 .. ",", param3)
end
---- 唯一一个入参,则会将所有的返回值作为入参
params(f2()) --> params:peng yong,28, nil
-- f2() 虽然返回两个参数,但是因为不是 最后一个参数(也不是唯一一个参数) 所以只会使用第一个返回值,则这里只是入参了两个参数
params(f2(), 10000) --> params:peng yong,10000, nil
---- f2() 最后一个参数,所以会进行多返回值展开,将所有的 f2() 返回值当作入参,所以这里就有三个入参值
params(10001, f2()) --> params:10001,peng yong, 28
-- 多值返回和其他的运算操作只会使用第一个返回值
print(f2() .. 1) --> peng yong1
-- 可以作为表的构造器,但也遵循同样的规则,只有作为最后一个表达式,才会使用所有返回值
t1 = { f2() }
t2 = { f2(), "jiang pengyong" }
showTable(t1) --> [1]=peng yong, [2]=28,
showTable(t2) --> [1]=peng yong, [2]=jiang pengyong,
使用括号屏蔽多值返回,则只会使用第一个返回值
-- 使用括号,强制只返回一个值,f2() 则只有第一个返回值被使用,其他的被省略
print((f2())) --> peng yong
如果 {...}
中包含有 nil ,则不再是一个有效的序列。 table.pack (5.2 引入) 会将所有的参数(包括 nil),保存到一个 table 中并返回,而且会有一个额外的字段 “n” 用于保存参数个数 。
function noNils(...)
local arg = table.pack(...)
local argValue = ""
for i = 1, arg.n do
argValue = argValue .. (arg[i] or "nil") .. " ,";
end
print("arg [" .. argValue .. "] size:" .. arg.n)
for i = 1, arg.n do
if arg[i] == nil then
return false
end
end
return true
end
-- 使用
print(noNils(2, 3, nil)) --> arg [2 ,3 ,nil ,] size:3 false
print(noNils(2, 3)) --> arg [2 ,3 ,] size:2 true
print(noNils(2, nil, 3)) --> arg [2 ,nil ,3 ,] size:3 false
print(noNils()) --> arg [] size:0 true
print(noNils(nil, nil)) --> arg [nil ,nil ,] size:2 false
作用是将 table 转换为一组返回值,可以作为函数的入参
unpack 函数的重要用途之一体现在泛型调用,泛型调用允许我们动态地调用具有任意参数的任意函数。
table.unpack(table)
将 table 进行展开,作为多值返回
table1 = { "jiang", "peng", "yong", "xiao" }
print(table.unpack(table1)) --> jiang peng yong xiao
table.unpack(table, startIndex, endIndex)
截取 table 的 startIndex 至 endIndex 区间的 item ,进行返回 ( [startIndex, endIndex] )
table1 = { "jiang", "peng", "yong", "xiao" }
print(table.unpack(table1, 2, 3)) --> peng yong
值得注意的是 table.unpack
使用长度操作符获取返回值的个数,因而该函数只能用于序列。
这样其实就可以动态的进行调用代码,将上面的稍作修改,可以通过将 print
赋予给变量在进行调用
f = print
f(table.unpack(table1)) --> jiang peng yong xiao
真正的 table.unpack
是由 c 进行编写,也可以用递归进行编写一个类似的
function unpack(t, i, n)
i = i or 1
n = n or #t
if i <= n then
return t[i], unpack(t, i + 1, n)
end
end
print(unpack(table1)) --> jiang peng yong xiao
print(unpack(table1, 2, 3)) --> peng yong
尾调用的作用不会使用额外的栈空间(这种实现称为尾调用消除)
因为当被调的函数执行结束后,程序就不再需要返回最初的调用者。
例如以下函数,当调用了 f 函数,f 函数执行完成 g 函数后,不再需要返回 f 函数,而是直接返回到调用 f 函数的地方。
function f(x)
x = x + 1;
return g(x);
end
由于尾调用函数不会使用栈空间,所以一个程序中能够嵌套的尾调用数量是无限的。
只有形如 return func(args)
的调用才算。
func 及其参数都可以有复杂的表达式,这些复杂的表达式会被先进行计算完之后才进行调用。
举些例子
下面的代码不是尾调用,因为 f 在返回前,还需要对 g 函数的返回进行结果丢弃。
function f(x)
g(x)
end
下面的代码不是尾调用,因为 g 函数的返回值还需要进一步处理(因为加了括号,会只使用一个返回值,所以也会进行舍弃多余的值),才能成为真正的返回值
return g(x) + 1
return x or g(x)
return (g(x))
下面的代码是尾调用,因为这里的 x[j] + a*b, i+j
可以先进行计算,x[i].foo
这里也是先获取,然后最后再进调用,所以无需在返回
return x[i].foo(x[j] + a*b, i+j)
Lua 项目地址:Github传送门 (如果对你有所帮助或喜欢的话,赏个star吧,码字不易,请多多支持)
公众号搜索 “江澎涌” 可以更快的获取到后续的更新文章