学习到Lua的函数,觉得有必要记下来。
参考教程:Programming in Lua
函数可以以表达式或陈述语句出现,如下所示:
print(8*9, 9/8)
a = math.sin(3) + math.cos(10)
书写函数时有个小规则,如果函数只有一个参数,或者是一串字符,或者是一个表结构时,括号可以省略:
print "Hello World" <--> print("Hello World")
dofile 'a.lua' <--> dofile ('a.lua')
f{x=10, y=20} <--> f({x=10, y=20})
type{} <--> type({})
对于Lua的面向对象,有一个特殊的语法来使用函数,就是使用冒号:
o:foo(x)
lua函数的一般写法:
function fun_name(<parameters>)
<body>
end
举个例子:
-- add the elements of sequence 'a'
function add (a)
local sum = 0
for i = 1, #a do
sum = sum + a[i]
end
return sum
end
调用Lua函数时,传递的参数和实际参数数目可以不一样,Lua会自动调整参数匹配。如果传递的参数比实际参数多了,那么多于的会被舍弃,少于的会得到nil值。
比如有个函数有下面几种情况:
f(3) --> 3 nil
f(3, 4) --> 3 4
f(3, 4, 5) --> 3 4 (5 is discarded)
这个特性可以被用来设置参数的默认值:
function incCount (n)
n = n or 1
count = count + n
end
lua的函数可以有多个返回值
比如lua库函数string.find:
s, e = string.find("hello Lua users", "Lua")
print(s, e) --> 7 9
lua函数的返回值数目也可以根据函数实际调用情况进行调整
比如下面三个函数:
function foo0 () end -- returns no results
function foo1 () return "a" end -- returns 1 result
function foo2 () return "a", "b" end -- returns 2 results
多重赋值时,要根据函数的调用位置来决定返回值的数目。函数调用在表达式末尾,则根据实际情况返回结果值。
x,y = foo2() -- x="a", y="b"
x = foo2() -- x="a", "b" is discarded
x,y,z = 10,foo2() -- x=10, y="a", z="b"
无返回值或返回值数目小于多重赋值数目,则多于的值为nil。
x,y = foo0() -- x=nil, y=nil
x,y = foo1() -- x="a", y=nil
x,y,z = foo2() -- x="a", y="b", z=nil
如果函数调用不是在表达式的末尾,那么函数只返回一个结果值。
x,y = foo2(), 20 -- x="a", y=20
x,y = foo0(), 20, 30 -- x=nil, y=20, 30 is discarded
当一个函数调用是另外一个函数的最后一个参数,那么第一个函数返回的所有结果都是另一个函数的参数。
print(foo0()) -->
print(foo1()) --> a
print(foo2()) --> a b
print(foo2(), 1) --> a 1
print(foo2() .. "x") --> ax (see next)
表构造能收集函数的所有返回值,不需要做出调整。
t = {foo0()} -- t = {} (an empty table)
t = {foo1()} -- t = {"a"}
t = {foo2()} -- t = {"a", "b"}
当然,这也只是针对函数处于表达式的末尾时:
t = {foo0(), foo2(), 4} -- t[1] = nil, t[2] = "a", t[3] = 4
形如return f()这个表达式也会返回f的所有结果值:
function foo (i)
if i == 0 then return foo0()
elseif i == 1 then return foo1()
elseif i == 2 then return foo2()
end
end
print(foo(1)) --> a
print(foo(2)) --> a b
print(foo(0)) -- (no results)
print(foo(3)) -- (no results)
当然也可以通过一个括号来只返回一个结果值:
print((foo0())) --> nil
print((foo1())) --> a
print((foo2())) --> a
有个特别的函数table.unpack就是有多个返回值:
print(table.unpack{10,20,30}) --> 10 20 30
a,b = table.unpack{10,20,30} -- a=10, b=20, 30 is discarded
unpack能帮助你实现一个调用含有任意参数的任意函数机制,说白了,能实现函数的动态调用,还是看例子。
f(table.unpack(a))
这个函数将a中的所有值作为f函数的参数。
下面看这个函数调用:
print(string.find("hello", "ll"))
你可以动态的进行改写:
f = string.find
a = {"hello", "ll"}
print(f(table.unpack(a)))
通常来说,unpack使用长度运算符来确定元素的返回数目,所以它是针对适当的序列来说的。但是如果需要,可以加以限制:
print(table.unpack({"Sun", "Mon", "Tue", "Wed"}, 2, 3))
--> Mon Tue
参数可变的函数
lua的函数可以拥有可变参数,也就是参数数目可变。print就是这样一个函数。
下面是用Lua函数实现的可变参数函数例子:
function add (...)
local s = 0
for i, v in ipairs{...} do
s = s + v
end
return s
end
print(add(3, 4, 10, 25, 12)) --> 54
…这三点就是表明一个函数有可变参数。…叫做参数变量表达式,表现类似一个返回所有结果值的函数。
可以模仿函数参数传递机制:
function foo (a, b, c)
变为:
function foo (...)
local a, b, c = ...
再看:
function id (…) return … end
function foo1 (...)
print("calling foo:", ...)
return foo(...)
end
上面这个机制可以用来进行跟踪调试。下面是另外一个有用的例子,结合string.format与io.write函数:
function fwrite (fmt, ...)
return io.write(string.format(fmt, ...))
end
注意上面的三点在fmt之后。因为Lua函数中可变参数这部分之前可以是参数数目确定的部分。
CALL PARAMETERS
fwrite() fmt = nil, no extra arguments
fwrite("a") fmt = "a", no extras
fwrite("%d%d", 4, 5) fmt = "%d%d", extras = 4 and 5
有极少数情况下,可变参数中可能有nil值,这时候{…}就不适用了。这种情况下,就要用table.pack函数,这个函数接收任意数目参数然后返回一个新的表。但是这个表有个额外的n区域,表示元素数目。
下面的例子测试是否有参数为nil:
function nonils (...)
local arg = table.pack(...)
for i = 1, arg.n do
if arg[i] == nil then return false end
end
return true
end
print(nonils(2,3,nil)) --> false
print(nonils(2,3)) --> true
print(nonils()) --> true
print(nonils(nil)) --> false
在可变参数中不会有nil值的情况下{…}比table.pack(…)速度快多了。
命名参数
Lua中函数参数传递是基于位置的。但是,也可以通过名字来定义参数,这在某些时候很有用,比如os.rename函数,对一个文件重命名。通常,我们不知道哪个文件名在前。这是,通过名字就很有用了:
rename{old="temp.lua", new="temp1.lua"}
function rename (arg)
return os.rename(arg.old, arg.new)
end
这种风格的函数传递在函数有很多参数且它们其中大部分都是可选项时是非常有用的。比如GUI编程中。