函数进阶
1.参数的简化:函数若只有一个参数,且此参数是一个字符串或者table构造式,则(实参)圆括号可以省略。建议不使用简化
例如:
function SetName(str)
print(str)
end
--SetName("test") 这是未简化的
SetName "test" 这是简化后的
--table类型简化
function Set(namearray)
for k,v in pairs(namearray) do
print(v)
end
end
--array={"1","2","3"}
--Set(array) 这是未简化的
Set {"t","e","s"} 这是简化的
2.可变参数:又称为变长参数,使用符号...表示多个参数,主要应用于形参中,这里的关键字功能类似C#中的para关键字
例如
function MultiParaVal(...)
for k,v in pairs({...}) do --使用{...}访问变长参数
print(v)
end
end
MultiParaVal("asd","qwe","zxc")
注意:A.在lua5.0以上版本通过局部table变量“arg”可以接收所有变长参数。arg是lua的内置函数,本质是把可变参数封装为一个表,#arg可表示参数个数;
访问变长参数时,使用{...}或者arg访问
例如
function MultiParaVal(...)
--for k,v in pairs({...}) do
-- print(v)
--end
for k,v in pairs(arg) do --arg可表示{...}
print(v)
end
end
MultiParaVal("asd","qwe","zxc")
注意:B.如果变长参数中包含nil,则必须使用“select”来访问变长参数。调用select时必须传入一个固定的实参select(选择开关)和一系列变长参数。
格式:1.select(index,...)返回从index下标开始,一直到变长参数列表结尾的所有元素
2.select(‘#’,...)返回变长参数列表长度
例如
function MultiParaVal(...)
print(select('#',...))
print(select(1,...))
end
MultiParaVal("asd","qwe","zxc",nil,"jkl")
结果为 5 asd qwe zxc nil jkl
3.标准函数库:lua的标准函数库也是lua 的内置函数,主要分三大类:
数学函数
print(math.abs(-88)) --求绝对值
print(math.max(12,55,62,32)) --求最大值
print(math.min(12,55,62,32)) --求最小值
print(math.sin(0)) --求sin
print(math.cos(0)) --求cos
print(math.sqrt(36)) --求平方根
print(math.random(1,10)) --求随机数,有问题
print(math.floor(18.88)) --取整
......
字符串函数
print(string.lower("weTySD")) --全部小写
print(string.upper("weTySD")) --全部大写
--还有很多
操作系统库
print(os.date()) --得到日期
print(os.time()) --得到时间,得到的不是常规的时间
应用案列:使用操作系统的时间系统函数,配合随机函数,开发准确的随机数函数
function GetRandom(min,max)
--得到时间字符串
local strtime=tostring(os.time())
print(strtime)
--得到一个反转字符串
local strRev=string.reverse(strtime)
print(strRev)
--得到前6位
local strRandomTime=string.sub(strRev,1,6)
print(strRandomTime)
--设置时间种子
math.randomseed(strRandomTime)
print(math.random(min,max))
end
GetRandom(50,100)
非真随机,结果明显几张在50~70间
4.函数尾调用:lua中可以再一个函数中,使用return返回另一个函数,这种预防成为“尾调用”。形式如:function f(x) return g(x) end
在递归算法中,因为调用g(x)后,f(x)中不再执行任何代码,所以不需要保留f(x)的调用桟信息;Lua做了这样的优化,称为"尾调用消除",g(x)返回后,控制点直接返回到调用f(x)的地方。尾调用不占用“堆栈”空间,所以不会出现“栈溢出”,可以起到优化存储空间的作用,例如
function f(n)
if n <= 0 then
return 0
end
a = f(n-1)
return n * a --非尾调用,当递归次数过多则报错"栈溢出"
end
--print(f(50000))
function f1(n)
if n <= 0 then
return 0
end
return f1(n-1) --尾调用,递归次数多也不会栈溢出
end
print(f1(50000))
尾调用时可以加()包住返回的方法,其作用是只返回方法的一个结果,形式为return(g())
例如
function func()
print("func")
--return fund() --此方法返回方法的所有结果:func fund 100 200
return (fund()) --此方式只返回方法的一个结果: func fund 100
end
function fund()
print("fund")
return 100,200
end
print(func())
5.函数的本质:lua函数本质是匿名的,即没有名称,讨论一个函数本质是讨论一个持有此函数的变量。
函数与普通类型的数值权利相同。
- 可以用一个变量或table指向函数,也可以作为实参传递给其他函数,还可以作为其他函数的返回值
- 本质上函数就是一条语句,可以将其存储在全局变量中,也可以存储在局部变量中
例如
function fun(num)
return num+1
end
array={}
array[1]=function(num) --方法1
return num*2
end
array[2]=function(num)
return num
end
array[3]=fun --方法2
for k,v in pairs(array) do
print(v(5))
end
print(array[1](10))
6.闭包:一个函数中嵌套子函数,子函数可以使用父函数中的局部变量,这种行为就叫做闭包,可以简略归纳为:闭包=函数+引用环境(非局部变量)
闭包的特点:闭包中的内嵌函数可以访问外部函数已经创建的所有局部变量,这些变量称为内嵌函数的“upValue”
闭包与一般函数的区别:闭包只是在形式和表现上像函数,但是实际上不是函数,函数只有一个实例,定义以后逻辑就确定了,不会执行时发生变化
“词法域”概念,若将一个函数写在另一个函数内,那么这个位于内部的函数便可以访问外部函数的局部变量,这项特征叫做“词法域”(就是闭包),外部函数的局部变量在匿名函数内既不是全局变量也不是局部变量,称其为“非全局变量”
闭包的典型应用:迭代器的实现可以记住闭包函数实现,闭合函数能保持每次调用间的一些状态
例1:无参数闭包
function Fun()
local i=0 --i变量成为内嵌函数的"upValue"
--这里的i既不是全局变量,也不是局部变量
--这里i称为“非局部变量”
print("--A--i=",i)
return function() --内部嵌入的匿名函数
print("--B--i=",i)
i=i+1
print("--c--i=",i)
return i
end
end
v=Fun()
print(v())
print(v())
print(v())
结果为
--A--i= 0
--B--i= 0
--c--i= 1
1
--B--i= 1
--c--i= 2
2
--B--i= 2
--c--i= 3
3
例2:带参数闭包
function Fun(i) --参数i是内嵌函数的"upValue"
print("a/i=",i)
return function()
print("b/i=",i)
i=i+1
print("c/i=",i)
return i
end
end
f=Fun(10) --此时已经执行Fun函数,打印了a/i= 10,然后返回的是一个 函数,将函数赋值给了f,f也就是一个函数了
print(f()) --这里f()执行了函数,打印了b/i= 10,然后i加了1,又 打印了c/i= 11,然后返回了i,由最后的print打印了i的值
print(f()) --重复上面的过程,注意此时i已经加了1
print(f())
结果为
a/i= 10 --执行f=Fun(10)时打印
b/i= 10
c/i= 11 --执行print(f())时打印,下面的同理
11
b/i= 11
c/i= 12
12
b/i= 12
c/i= 13
13
例3:具备多个内嵌函数的闭包
function Fun()
local num=10 --两个内嵌函数对upValue进行共享处理
function In1() --内嵌函数1,负责打印
print(num)
end
function In2() --内嵌函数2,负责计算
num=num+10
end
return In1,In2
end
v1,v2=Fun()
v1()
v2()
v1()
结果为10 20
例4:带参数的内嵌函数
function Fun(num)
print("a/num=",num)
return function(value)
print("b/num=",num)
num=num*value
print("c/num=",num)
return num
end
end
fun=Fun(4)
print(fun(2))
print(fun(2))
print(fun(2))
结果为
a/num= 4
b/num= 4
c/num= 8
8
b/num= 8
c/num= 16
16
b/num= 16
c/num= 32
32
例5:闭包具备多个实例
function Fun(num)
print("a/num=",num)
return function(value)
print("b/num=",num)
num=num*value
print("c/num=",num)
return num
end
end
fun=Fun(4) --执行到这里时,num初始化为4,打印a/num=4
print(fun(2)) --此时打印b/num=4,num赋值为4*2,打印c/num=8,然后返 回num,打印8
print(fun(2)) --步骤同上
Fun1=Fun(5) --执行到这时num被重新初始化为5,接下来步骤同上
print(fun1(2))
print(fun1(2))
结果为
a/num= 4
b/num= 4
c/num= 8
8
b/num= 8
c/num= 16
16
a/num= 5
b/num= 5
c/num= 10
10
b/num= 10
c/num= 20
20
例6:使用闭包实现自定义迭代器
function Fun(array)
local i=0
return function()
i=i+1
return array[i]
end
end
ar={1,2,5,8,9,10}
f=Fun(ar)
--方式1使用while
--while(true) do
-- local v=f()
-- if(v==nil) then
-- break
-- end
-- print(v)
--end
--方式2使用for
for m in Fun(ar) do
print(m)
End
结果为:1 2 5 8 9 10
7.加载其他lua脚本
使用require(“路径”)这个方法可以运行指定lua脚本,其中路径为本文件的相对路径,若需要运行的文件与本文件同目录,则直接填入需要运行的文件名称不加”.lua”后缀,例如require("Test")以及require("Test1/Test")(不同目录)
注意点:
文件名不能带有特殊字符例如”.”等,“/”除外,否则会导致加载错误
文件拓展名.lua不需要编写,因为会自动添加
同一个文件无法同时被加载多次,因为存在一个表会记录已经加载得到文件,这个表就是package.loaded[“文件相对路径”],
例如print(package.loaded["Test1/Test"])结果为True,当每次加载文件时,都会检查package.loaded里面的内容,若为True则不加载否则加载,这样可以防止重复加载。其中,package.loaded中存储的是程序员所写的文件的路径,若同一个文件加载所写的路径不同可以重复加载,例如require("Test1/Test")和require("./Test1/Test"),加载同一个文件,但是路径写法不同,可以重复加载。若想重复加载一个文件,则需要在加载前清除这个状态,清除这个状态的方法为设置其为nil或false,
例如package.loaded["Test1/Test"] = nil 或package.loaded["Test1/Test"] = false
8.获取其他文件的局部变量
例如有一个lua2.lua脚本,存在一个局部变量config,
local config = {}
config.appName = "mlg"
此时要想其他文件拿到这个config局部变量需要lua2中返回这个变量
local config = {}
config.appName = "mlg"
return config
这时使用local fig = require(“lua2”),可以拿到lua2中的局部变量,就能使用了
9.元表
local t1 = {1,2,3}
local t2 = {2,3,4}
--直接打印表,结果为表的内存地址
--print(t1)
--我们希望打印table时,打印为表的内容,如{1,2,3}
--打印时将表一字符串输出
--为了实现这种方法,lua提供了这种语法特性,元表拓展(metatable拓展)
local meta = { --拥有拓展t1的元表,只要在元素中实现一些特殊的函数,则
--t1就能实现一些特殊的功能,比如让t1作为字符串使用
__tostring = function(t) --元方法,当被拓展表被以string方式调用时调用
local format = "{";
for k,v in pairs(t) do
format = format..v..",";
end
format = format.."}";
return format
end
}
setmetatable(
t1, --需要进行元表拓展的数据表
meta
)
setmetatable(
t2, --需要进行元表拓展的数据表
meta
)
print(t1)
print(t2)
其中__tostring是固定的元方法,还有__add(相当于实现加法操作,至于如何加自定义)等