第五章 函数

来源:互联网 发布:网络8888端口 编辑:程序博客网 时间:2024/06/02 00:28
在Lua中,函数是对语句和表达式进行抽象的主要机制。
即使调用函数时没有参数,也必须要写出一对空括号。一种特殊的例外情况:
一个函数若只有一个参数,且此参数是一个字面字符串或table构造式,那么圆括号别是可有可无的。
print "Hello World"
dofile 'test.lua'
print [[ a multi-line
message]]
f {x = 0, y = 1}
type {}

Lua为面向对象式的调用也提供了一种特殊的语法---冒号操作符。表达式o.foo(o, x)的另一种写法是o:foo(x),冒号操作符使调用o.foo时将o隐含地作为函数的第一个参数。

多重返回值
Lua允许函数返回多个结果,只需要在return关键字后列出所有的返回值即可。
s, e = string.find("Hello Lua users", "Lua")
print(s, e) -->7 9

function maximum(a)
local mi = 1 --最大值索引
local m = a[mi] --最大值
for i, val ipairs(a) do
if m < val then
m = val
mi = i
end
end
return mi, m
end
print(maximum({10, 20, 30, 4, 1, 12, 50, 22})) -->7 50
若将函数单独作为一条语句时,Lua会丢弃函数的所有返回值。
若将函数作为表达式的一部分时,Lua只保留函数的第一个返回值。
只有当一个函数调用是一系列表达式中的最后一个元素(或仅有一个元素)时,才能获得它的所有返回值。
一系列表达式包括4种情况:多重赋值,函数调用时传入的实际参数列表,table构造式和return语句。
function foo0() end
function foo1() return "a" end
function foo2() return "a", "b" end
x, y, z = 10, foo2() --x = 10, y = a, z = b
x, y = foo2(), 20 --x = a, y = 20
x, y = foo0(), 20, 30 --x = nil, y = 20
print(foo2()) --a b
print(foo2() .. "x") --ax
t = {foo2()} --t = {"a", "b"}
t = {foo0(), foo2(), 4} --t[1] = nil, t[2] = "a", t[3] = 4
但是,可以将一个函数调用放入一对圆括号中,从而迫使它只返回一个结果。
print((foo2())) -->a
请注意return语句后面的内容是不需要圆括号的,在该位置上使用圆括号会导致不同的行为。例如return (f(x)),将只返回一个值,而无关乎f返回了几个值。
unpack函数----接受一个数组作为参数,并从下标1开始返回该数组的所有元素
print(unpack({10, 20, 30})) -->10 20 30
a, b = unpack({10, 20, 30}) --a = 10, b =20, 30被丢弃
unpack的一项重要用途体现在“泛型调用”机制中。
想调用任意函数f,而所有的参数都在数组a中,则:
f(unpack(a))
f = string.find
a = {"hello", "ll"}
print(f(unpack(a))) -->3 4

变长参数
function add(...)
local s = 0
for i, v in ipairs{...} do
s = s + v
end
return s
end
print(add(3, 4, 5, 6)) -->18
参数列表中的3个点(...)表示该函数可接受不同数量的实参。
一个函数如果需要访问它的变长参数时,仍需要使用3个点(...),此时的3个点是当做表达式来使用的。表达式{...}表示一个由所有变长参数构成的数组。
表达式“...”的行为类似于一个具有多重返回值的函数,它返回的是当前函数的所有变长参数。
local a, b = ...
使用select函数访问可能有nil值存在的变长参数列表:
调用select时,必须传入一个固定实参selector(选择开关)和一系列变长参数。如果selector为数字n,那么select返回它的第n个可变实参;否则,selector只能为字符串“#”,这样select会返回变长参数的总和。
for i = 1, select("#", ...) do
local arg = select(i, ...) --得到第i个参数
<循环体>
end
注意:Lua5.0中对于变长参数则有另外一套机制。Lua5.0并没有提供“..."表达式,而是通过一个隐含的局部table变量”table“来接受所有的变长参数。这个table还有一个名为“n”的字段,用来记录变长参数的 总数。
function foo(a, b, ...)
local arg = {...}; arg.n = select("#", ...)
<函数体>
end
这套机制的缺点在于,每当程序调用了一个具有变长参数的函数时,都会创建一个新的table。而在新机制中,只有在需要时才会去创建这个用于变长参数访问的table。

具名实参
Lua中的实参传递机制是具有”位置性“的。但有时候通过名称来指定实参也是很有用的。来思考一个函数os.rename,这个函数用于文件改名。通常会忘记第一个参数是表示新文件还是旧文件。因此希望这个函数能够接受两个具有实际名称的参数,例如:
--无效的演示代码
rename(old = "temp.lua", new = "temp1.lua")
Lua并不直接支持这种语法。但是可以通过细微的改变来获得相同的结果。主要是将所有实参组织到一个table中,并将这个table作为唯一的实参传递给函数。
rename{old = "temp.lua", new = "temp1.lua"}
将rename修改为只接受一个参数,并从这个参数中获取实际参数:
function rename(arg)
return os.rename{arg.old, arg.new}
end
若一个函数拥有大量参数,而其中大部分参数是可选参数的话,这种参数风格会特别有用。
例如:
w = Window{x = 0, y = 0, width = 300, height = 400,
title = "Lua", background = "blue",
brother = true}
function Window(option)
--检查必要的参数
if type(option.title) ~= "string" then
error("no title")
elseif type(option.width) ~= "number" then
error("no width")
elseif type(option.height) ~= "number" then
error("no height")
end
--其他参数都是可选的
_Window(option.title,
option.x or 0,
option.y or 0,
option.width, option.height,
option.background or "white",
option.brother
)
end

























0 0