1-5、Lua函數
函數有兩種用途:1.完成指定的任務,這種情況下函數作爲調用語句使用;2.計算並返回值,這種情況下函數作爲賦值語句的表達式使用。
語法:
function func_name (arguments-list)
statements-list;
end;
調用函數的時候,如果參數列表爲空,必須使用()表明是函數調用。
print(8*9, 9/8)
a = math.sin(3) + math.cos(10)
print(os.date())
上述規則有一個例外,當函數只有一個參數並且這個參數是字符串或者表構造的時候,()可有可無:
print "Hello World" <--> print("Hello World")
dofile 'a.lua' <--> dofile ('a.lua')
print [[a multi-line <--> print([[a multi-line
message]] message]])
f{x=10, y=20} <--> f({x=10, y=20})
type{} <--> type({})
Lua也提供了面向對象方式調用函數的語法,比如o:foo(x)與o.foo(o, x)是等價的,後面的章節會詳細介紹面向對象內容。
Lua使用的函數,既可是Lua編寫的,也可以是其他語言編寫的,對於Lua程序員,用什麼語言實現的函數使用起來都一樣。
Lua函數實參和形參的匹配與賦值語句類似,多餘部分被忽略,缺少部分用nil補足。
function f(a, b) return a or b end
CALL PARAMETERS
f(3) a=3, b=nil
f(3, 4) a=3, b=4
f(3, 4, 5) a=3, b=4 (5 is discarded)
5.1 多返回值
Lua函數可以返回多個結果值,比如string.find,其返回匹配串“開始和結束的下標”(如果不存在匹配串返回nil)。
s, e = string.find("hello Lua users", "Lua")
print(s, e) --> 7 9
Lua函數中,在return後列出要返回的值得列表即可返回多值,如:
function maximum (a)
local mi = 1 -- maximum index
local m = a[mi] -- maximum value
for i,val in ipairs(a) do
if val > m then
mi = i
m = val
end
end
return m, mi
end
print(maximum({8,10,23,12,5})) --> 23 3
Lua總是調整函數返回值的個數以適用調用環境,當作爲獨立的語句調用函數時,所有返回值將被忽略。假設有如下三個函數:
function foo0 () end -- returns no results
function foo1 () return 'a' end -- returns 1 result
function foo2 () return 'a','b' end -- returns 2 results
第一,當作爲表達式調用函數時,有以下幾種情況:
-
- 當調用作爲表達式最後一個參數或者僅有一個參數時,根據變量個數函數儘可能多地返回多個值,不足補nil,超出捨去。
-
- 其他情況下,函數調用僅返回第一個值(如果沒有返回值爲nil)
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'
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
第三,函數調用在表構造函數中初始化時,和多值賦值時相同。
a = {foo0()} -- a = {} (an empty table)
a = {foo1()} -- a = {'a'}
a = {foo2()} -- a = {'a', 'b'}
a = {foo0(), foo2(), 4} -- a[1] = nil, a[2] = 'a', a[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
一個return語句如果使用圓括號將返回值括起來也將導致返回一個值。
函數多值返回的特殊函數unpack,接受一個數組作爲輸入參數,返回數組的所有元素。unpack被用來實現範型調用機制,在C語言中可以使用函數指針調用可變的函數,可以聲明參數可變的函數,但不能兩者同時可變。在Lua中如果你想調用可變參數的可變函數只需要這樣:
f(unpack(a))
unpack返回a所有的元素作爲f()的參數
f = string.find
a = {"hello", "ll"}
print(f(unpack(a))) --> 3 4
預定義的unpack函數是用C語言實現的,我們也可以用Lua來完成:
function unpack(t, i)
i = i or 1
if t[i] then
return t[i], unpack(t, i + 1)
end
end
5.2 可變參數
Lua函數可以接受可變數目的參數,和C語言類似在函數參數列表中使用三點(…)表示函數有可變的參數。Lua將函數的參數放在一個叫arg的表中,除了參數以外,arg表中還有一個域n表示參數的個數。
例如,我們可以重寫print函數:
printResult = ""
function print(...)
for i,v in ipairs(arg) do
printResult = printResult .. tostring(v) .. "\t"
end
printResult = printResult .. "\n"
end
有時候我們可能需要幾個固定參數加上可變參數
function g (a, b, ...) end
CALL PARAMETERS
g(3) a=3, b=nil, arg={n=0}
g(3, 4) a=3, b=4, arg={n=0}
g(3, 4, 5, 8) a=3, b=4, arg={5, 8; n=2}
如上面所示,Lua會將前面的實參傳給函數的固定參數,後面的實參放在arg表中。
舉個具體的例子,如果我們只想要string.find返回的第二個值。一個典型的方法是使用啞元(dummy variable,下劃線):
local _, x = string.find(s, p)
-- now use `x'
...
還可以利用可變參數聲明一個select函數:
function select (n, ...)
return arg[n]
end
print(string.find("hello hello", " hel")) --> 6 9
print(select(1, string.find("hello hello", " hel"))) --> 6
print(select(2, string.find("hello hello", " hel"))) --> 9
有時候需要將函數的可變參數傳遞給另外的函數調用,可以使用前面我們說過的unpack(arg)返回arg表所有的可變參數,Lua提供了一個文本格式化的函數string.format(類似C語言的sprintf函數):
function fwrite(fmt, ...)
return io.write(string.format(fmt, unpack(arg)))
end
這個例子將文本格式化操作和寫操作組合爲一個函數。
5.3 命名參數
Lua的函數參數是和位置相關的,調用時實參會按順序依次傳給形參。有時候用名字指定參數是很有用的,比如rename函數用來給一個文件重命名,有時候我們我們記不清命名前後兩個參數的順序了:
-- invalid code
rename(old="temp.lua", new="temp1.lua")
上面這段代碼是無效的,Lua可以通過將所有的參數放在一個表中,把表作爲函數的唯一參數來實現上面這段僞代碼的功能。因爲Lua語法支持函數調用時實參可以是表的構造。
rename{old=“temp.lua”, new=“temp1.lua”}
根據這個想法我們重定義了rename:
function rename (arg)
return os.rename(arg.old, arg.new)
end
當函數的參數很多的時候,這種函數參數的傳遞方式很方便的。例如GUI庫中創建窗體的函數有很多參數並且大部分參數是可選的,可以用下面這種方式:
w = Window {
x=0, y=0, width=300, height=200,
title = "Lua", background="blue",
border = true
}
function Window (options)
-- check mandatory options
if type(options.title) ~= "string" then
error("no title")
elseif type(options.width) ~= "number" then
error("no width")
elseif type(options.height) ~= "number" then
error("no height")
end
-- everything else is optional
_Window(options.title,
options.x or 0, -- default value
options.y or 0, -- default value
options.width, options.height,
options.background or "white", -- default
options.border -- default is false (nil)
)
end