Lua基礎 函式(一)
轉自:http://blog.csdn.net/wzzfeitian/article/details/8653101
在Lua中,函式是對語句和表示式進行抽象的主要方法。既可以用來處理一些特殊的工作,也可以用來計算一些值。下面有3個例子,分別將函式當作一條語句;當作表示式(後面兩個是一類)。
- print(8*9, 9/8) --> 72 1.125
- a = math.sin(3) + math.cos(10) --> a = -0.69795152101659
-
print(os.date()) --> Sat Mar 9 12:14:08 2013
函式如果帶引數,那麼就要用(arg1, arg2,...)括起來,如果沒有引數,就寫個空(),說明這是個函式呼叫。
特例,如果函式只有一個引數,並且引數型別是字串或者table,那麼()可以省略,如下示例:
- 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程式可以呼叫C語言或者Lua實現的函式。Lua基礎庫中的所有函式都是用C實現的。呼叫一個用C實現的函式,和呼叫一個用Lua實現的函式,二者沒有任何區別。
Lua函式的定義語法比較常規,如下示例:
- function add(a)
-
local sum = 0
- for i, v in ipairs(a) do
- sum = sum + v
- end
- return sum
- end
函式的引數跟區域性變數一樣,用傳入的實參來初始化,多餘的實參被丟棄,多餘的形參初始化為nil。示例如下:
- function f(a, b) return a or b end
- f(3) -- a=3, b=nil
- f(3, 4) -- a=3, b=4
- f(3, 4, 5) -- a=3, b=4 (5被丟棄)
雖然Lua可以處理這樣的情況,但是不鼓勵這種傳入錯誤數量引數的函式呼叫,可能會使程式執行時有點小問題。不過,有些情況下,這個特性可以加以利用,例如下面示例的預設引數:
1.多返回值
不同於常規函式,Lua的函式可以返回多個返回值。一些Lua中預定義的函式可以返回多個返回值。例如string.find函式,在string中匹配一個sub-string,string.find返回sub-string的起始位置和結束位置。利用多賦值語句來獲取函式的多個返回值。
用Lua寫的函式也可以返回多個返回值,如下示例,查詢array中的最大值,並返回其位置和值
Lua會根據實際情況來使函式的返回值個數適應呼叫處的期望。
1)如果一個函式呼叫作為一條語句,所有的返回值都被丟棄
2)如果一個函式呼叫作為一個表示式,除了3)的情況,返回值只保留第一個
3)在多賦值,返回值作為實參來呼叫其他函式,table中,return語句中,這4種呼叫場景,如果函式呼叫作為其最後一個表示式,那麼會保留所有的返回值,然後根據實際呼叫需要再糾正。
示例:
- -- 多賦值,函式呼叫是最後一個表示式
- 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(1, foo2()) --> 1 a b
- print(foo2() .. "x") --> ax (see next)
- -- table中
- t = {foo0()} -- t = {} (an empty table)
- t = {foo1()} -- t = {"a"}
- t = {foo2()} -- t = {"a", "b"}
- -- table中,但是函式呼叫不是最後一個表示式
- t = {foo0(), foo2(), 4} -- t[1] = nil, t[2] = "a", t[3] = 4
- -- return語句中
- 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可以返回多個值,它傳入一個array,然後返回array中的每一個值。
- print(unpack{10,20,30}) --> 10 20 30
- a,b = unpack{10,20,30} -- a=10, b=20, 30 is discarded
unpack的一個重要用法是泛型呼叫,提供了比C語言中更大的靈活性。在Lua中,如果你想呼叫一個函式f,傳入可變數量的引數,很簡單,
- f(unpack(a))
unpack返回a中的所有值,並傳給f 作為引數,下面示例:
- f = string.find
- a = {"hello", "ll"}
- print(f(unpack(a))) --> 3 4
Lua中的unpack是用C實現的。其實我們也可以用Lua來實現它
- function unpack (t, i)
- i = i or 1
- if t[i] then
- return t[i], unpack(t, i + 1)
- end
- end
2. 變參
Lua中的一些函式接受可變數量的引數,例如print函式。print函式是用C來實現的,但是我們也可以用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
引數列表中的'...'指明該函式可以接受變參。我們可以將‘...’當作一個表示式,或者一個多返回值的函式(返回當前函式的所有引數)。例如
- local a, b = ...
用可選引數的前兩個初始化區域性變數a,b的值。再看下面的例子,
- function foo(a, b, c) <==> function foo(...) local a, b, c = ...
- function id (...) return ... end
上面這個函式簡單地返回它所有的引數。下面的例子,說明了一個跟蹤函式呼叫的技巧
- function foo1 (...)
- print("calling foo:", ...)
- return foo(...)
- end
再看一個實用的例子。Lua提供了不同的函式來格式化文字string.formant和寫文字io.write,我們可以簡單地將二者合二為一。
- function fwrite (fmt, ...)
- return io.write(string.format(fmt, ...))
- end
注意有一個固定的引數fmt。變參函式可能含有不定數目的固定引數,後面再跟變參。Lua會將前面的實參賦值給這些固定引數,剩下的實參才能當作變參看待。下面是幾個示例:
- CALL PARAMETERS
- fwrite() -- fmt = nil, no varargs
- fwrite("a") -- fmt = "a", no varargs
- fwrite("%d%d", 4, 5) -- fmt = "%d%d", varargs = 4 and 5
如果想要迭代處理變參,可以用{...}來將所有的變參收集到一個table中。但是有時變參中可能含有非法的nil,我們可以用select函式。select函式有一個固定的引數selector,然後跟一系列的變參。呼叫的時候,如果selector的值為數字n,那麼select函式返回變參中的第n個引數,否則selector的值為'#',select函式會返回可變引數的總數目。下面示例:
- for i=1, select('#', ...) do
- local arg = select(i, ...) -- get i-th parameter
- <loop body>
- end
注意,select("#", ...)返回變參的數目,包括nil在內。
3. 帶名字的引數
Lua中函式的引數傳遞是基於位置的,當呼叫函式的時候,實參根據位置來匹配形參。但是,有的時候,根據名字來匹配更實用。例如,系統函式os.rename,我們會經常忘記新名字和舊名字哪個在前;為了解決這個問題,我們嘗試重新定義這個函式。下面這個
- -- invalid code
- rename(old="temp.lua", new="temp1.lua")
上面這個程式碼是非法的,Lua並不支援這樣的語法。但是我們可以修改一點點,來實現相同的效果。
- 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
- }
Window函式可以檢查必須的引數,並且給可選引數賦予預設值等。假設_Window函式可以用來建立一個新視窗,但是它必須要全部的引數。那我們就可以重新定義一個Window函式如下:
- 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