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