Lua中的函数(function)、可变参数、局部函数、尾递归优化等实例讲解
一、函数
在Lua中,函数是作为"第一类值"(First-ClassValue),这表示函数可以存储在变量中,可以通过参数传递给其他函数,或者作为函数的返回值(类比C/C++中的函数指针),这种特性使Lua具有极大的灵活性。
Lua对函数式编程提供了良好的支持,可以支持嵌套函数。
另外,Lua既可以调用Lua编写的函数,还可以调用C语言编写的函数(Lua所有的标准库都是C语言写的)。
定义一个函数
functionhello() print('hello') end
hello函数不接收参数,调用:hello(),虽然hello不接收参数,但是还可以可以传入参数:hello(32)
另外如果只传递一个参数可以简化成functionnamearg的调用形式(注意数值不行)
>hello'3' hello >hello{} hello >hello3 stdin:1:syntaxerrornear'3'
另外对变量名也不适用
>a=21 >printa stdin:1:syntaxerrornear'a'
另外,Lua函数不支持参数默认值,可以使用or非常方便的解决(类似Javascript)
>functionf(n) >>n=nor0 >>print(n) >>end >f() 0 >f(1) 1
Lua支持返回多个值,形式上非常类似Python:
>functionf() >>return1,2,3 >>end >a,b,c=f() >print(a..b..c) 123
函数调用的返回值可以用于table:
>t={f()} >print(t[1],t[2],t[3]) 1 2 3
可见,f()返回的三个值分别称为table的3个元素,但是情况并不总是如此:
>t={f(),4} >print(t[1],t[2],t[3]) 1 4 nil
这次,f()返回的1,2,3只有1称为table的元素;
>t={f(),f()} >print(t[1],t[2],t[3],t[4],t[5]) 1 1 2 3 nil
总之:只有最后一项会完整的使用所有返回值(假如是函数调用)。
对于无返回值的函数,可以使用(f())的形式强行返回一个值(nil)
>functiong() >>end >print(g()) >print((g())) nil
实际上,(f())形式的调用返回一个且只返回一个值
>print((f())) 1 >print(f()) 1 2 3
二、变长参数
Lua支持编程参数,使用简单(借助于table、多重赋值)
>functionf(...) fork,vinipairs({...})do print(k,v) end end >f(2,3,3) 1 2 2 3 3 3
使用多重赋值的方式
>functionsum3(...) >>a,b,c=... >>a=aor0 >>b=bor0 >>c=cor0 >>returna+b+c >>end >=sum3(1,2,3,4) 6 >returnsum3(1,2) 3
通常在遍历变长参数的时候只需要使用{…},然而变长参数可能会包含一些nil;那么就可以用select函数来访问变长参数了:select('#',…)或者select(n,…)
select('#',…)返回可变参数的长度,select(n,…)用于访问n到select('#',…)的参数
>=select('#',1,2,3) 3 >returnselect('#',1,2,nil,3) 4 >=select(3,1,2,nil,3) nil 3 >=select(2,1,2,nil,3) 2 nil 3
注意:Lua5.0中没有提供…表达式,而是通过一个隐含的局部table变量arg来接收所有的变长参数,arg.n表示参数的个数;
三、函数式编程
函数做一个First-ClassValue可以赋值给变量,用后者进行调用
>a=function()print'hello'end >a() hello >b=a >b() hello
匿名函数
>g=function()returnfunction()print'hello'endend >g()() hello
函数g返回一个匿名函数;
闭包是函数式编程的一种重要特性,Lua也支持
>g=function(a)returnfunction()print('hello'..a);a=a+1endend >f=g(3) >f() hello3 >f() hello4
四、局部函数
局部函数可以理解为在当前作用域有效的函数,可以用local变量来引用一个函数:
>do >>locallf=function()print'hello'end >>lf() >>end hello >lf() stdin:1:attempttocallglobal'lf'(anilvalue) stacktraceback: stdin:1:inmainchunk [C]:in?
需要注意的是,对于递归函数的处理
>do locallf=function(n) ifn<=0then return end print'hello' n=n-1 lf(n) end lf(3) end hello stdin:8:attempttocallglobal'lf'(anilvalue) stacktraceback: stdin:8:infunction'lf' stdin:9:inmainchunk [C]:in?
而应该首先声明locallf,在进行赋值
do locallf; lf=function(n) ifn<=0then return end print'hello' n=n-1 lf(n) end lf(3) end hello hello hello
Lua支持一种localfunction(…)…end的定义形式:
>do localfunctionlf(n) ifn<=0then return end print'hello' n=n-1 lf(n) end lf(3) end hello hello hello >lf(3) stdin:1:attempttocallglobal'lf'(anilvalue) stacktraceback: stdin:1:inmainchunk [C]:in?
五、尾调用
所谓尾调用,就是一个函数返回另一个函数的返回值:
functionf() … returng() end
因为调用g()后,f()中不再执行任何代码,所以不需要保留f()的调用桟信息;Lua做了这样的优化,称为"尾调用消除",g()返回后,控制点直接返回到调用f()的地方。
这种优化对尾递归非常有益,通常递归意味着调用桟的不断增长,甚至可能造成堆栈溢出;而尾递归提供了优化条件,编译器可以优化掉调用桟。
下面的递归函数没有使用尾递归,而参数为大数时,堆栈溢出:
>functionf(n) >>ifn<=0then >>return0 >>end >>a=f(n-1) >>returnn*a >>end >f(10000000000) stdin:5:stackoverflow stacktraceback: stdin:5:infunction'f' stdin:5:infunction'f' stdin:5:infunction'f' stdin:5:infunction'f' stdin:5:infunction'f' stdin:5:infunction'f' stdin:5:infunction'f' stdin:5:infunction'f' stdin:5:infunction'f' stdin:5:infunction'f' ... stdin:5:infunction'f' stdin:5:infunction'f' stdin:5:infunction'f' stdin:5:infunction'f' stdin:5:infunction'f' stdin:5:infunction'f' stdin:5:infunction'f' stdin:5:infunction'f' stdin:5:infunction'f' stdin:1:inmainchunk [C]:in?
优化为尾递归
functionf(n,now) ifn<=0then returnnow end returnf(n-1,now*n) end f(10000000000,1)
运行n久也无堆栈溢出;