Lua极简入门指南(一):函数篇
Lua和其他很多语言一样,函数调用时参数列表被包裹在括号中:
print('HelloWorld')
特别的情况是,如果函数调用时只有一个参数,并且此参数为字符串literal(字面量)或者table构造器(constructor)时,包裹参数的括号可以省略:
print'HelloWorld'<-->print('HelloWorld') type{} <-->type({})
Lua为面向对象的调用提供了特殊的语法:
o:foo(x)<-->o.foo(o,x)
Lua调用的函数可能被定义在Lua中,也可能被定义在C中(Lua标准库中的所有函数都使用C编写)。
函数的定义
functionadd(a) localsum=0 fori=1,#ado sum=sum+a[i] end returnsum end print(add{1,2,3})
函数调用时,实参(arguments)和形参(parameters)个数可以不匹配,多余的实参会被丢弃,多余的形参值为nil,例如:
functionf(a,b)print(a,b)end f(3) -->3 nil f(3,4) -->3 4 f(3,4,5) -->3 4
函数多值返回
在Lua中函数可以返回多个值。例如:
functionmaximum(a) localmi=1 localm=a[mi] fori=1,#ado ifa[i]>mthen mi=i;m=a[i] end end returnm,mi end print(maximum{8,10,23,12,5})
在多赋值时,多余的值会被丢弃,不足时变量值为nil:
x,y=1 -->x==1,y==nil x,y=1,2,3 -->x==1,y==2
在函数调用时形参的值处理上,在函数返回值的获取上,都遵循这个规则。例如:
functionfoo0()end functionfoo1()return'a'end functionfoo2()return'a','b'end x,y=foo2() -->x=='a',y=='b' x=foo2() -->x=='a' x,y,z=10,foo2() -->x==10,y=='a',z=='b' t={foo2()} -->{'a','b'}
再看一个例子:
functionfoo2()return'a','b'end x,y=foo2(),20 -->x=='a',y==20
这里,由于函数调用不是在列表的最后一个位置,这时候函数只提供一个值。一个更有意义的例子:
functionfoo2()return'a','b'end print(foo2()) -->a b print(foo2(),1) -->a 1
如果函数调用在列表的最后一个位置,同时使用()包裹函数调用,这时候函数也只提供一个值:
functionfoo2()return'a','b'end print(foo2()) -->a b print((foo2())) -->a
一个比较有用的利用函数多值返回的特性的函数是table.unpack,它接受一个数组作为参数,返回数组中的所有元素:
print({10,20,30}) -->table:00000000005BBE00 print(table.unpack{10,20,30}) -->10 20 30
变长参数
我们在使用print函数的时候可以传递任意数目的参数。Lua提供了…表示参数列表,让我们实现类似print的函数:
functionadd(...) locals=0 for_,vinipairs{...}do s=s+v end returns end print(add(3,4,5)) -->12
再一个例子:
functiontest(...) locala,b=... print(a,b) end test(1,2,3) -->1 2
还有一个特殊情况,我们需要注意:
functionp(...) for_,vinipairs{...}do print(v) end end p(1,nil,3) -->1 print(1,nil,3) -->1nil3
上例可以看到,我们的p函数在参数中存在nil时并非按我们的意愿输出了结果。Lua提供了一个table.pack函数,用于获取其调用参数(包括nil参数)并返回一个包含所有参数的table,此table存在一个额外的域n,用于表示参数的数量:
functionp(...) localarg=table.pack(...) fori=1,arg.ndo print(arg[i]) end end p(1,nil,3,nil) -->1nil3nil
不过需要注意的是,{…}相比table.pack(…)来说更加高效,我们可以在确保没有nil参数的时候使用。
函数是第一类值(first-classvalues)
我们能够像使用其他变量一样的使用函数:
a={p=print} a.p('HelloWorld') -->HelloWorld print=math.sin a.p(print(1)) -->0.8414709848079
类似于{}作为table的构造器,我们可以认为function(x)end为函数的构造器:
localadd=function(a,b) returna+b end print(add(1,2)) --另一种写法 localfunctionadd(a,b) returna+b end
table.sort函数用于排序,它可以接受一个排序函数作为参数:
network={ {name="grauna",IP="210.26.30.34"}, {name="arraial",IP="210.26.30.23"}, {name="lua",IP="210.26.23.12"}, {name="derain",IP="210.26.23.20"}, } print('--------------') for_,vinipairs(network)do print(v.name) end table.sort(network,function(a,b) returna.name>b.name end) print('--------------') for_,vinipairs(network)do print(v.name) end
输出结果为:
-------------- grauna arraial lua derain -------------- lua grauna derain arraial
在此例中,我们提供的排序函数作为一个参数传递给table.sort函数,像此排序函数这样没有名字的函数被叫做匿名函数。
闭包(closures)
很多语言都支持闭包(Golang、JavaScript等)。一个函数和其访问的外部变量组成一个闭包。看一个例子:
functionnewCounter() locali=0 returnfunction() i=i+1 returni end end c1=newCounter() print(c1()) -->1 print(c1()) -->2
这里的c1就是一个闭包(外部变量为i),每次调用newCounter都会创建一个闭包(并创建一个新的变量i):
c2=newCounter() print(c2()) -->1 print(c1()) -->3 print(c2()) -->2