第五章 函數

在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

























發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章