1. 程式人生 > >Lua基礎 函式(一)

Lua基礎 函式(一)

轉自:http://blog.csdn.net/wzzfeitian/article/details/8653101

在Lua中,函式是對語句和表示式進行抽象的主要方法。既可以用來處理一些特殊的工作,也可以用來計算一些值。下面有3個例子,分別將函式當作一條語句;當作表示式(後面兩個是一類)。

  1. print(8*9, 9/8)                  --> 72  1.125  
  2. a = math.sin(3) + math.cos(10)   --> a = -0.69795152101659  
  3. print(os.date())                 --> Sat Mar  9 12:14:08 2013  

函式如果帶引數,那麼就要用(arg1, arg2,...)括起來,如果沒有引數,就寫個空(),說明這是個函式呼叫。

特例,如果函式只有一個引數,並且引數型別是字串或者table,那麼()可以省略,如下示例:

  1. print "Hello World"   <==>   print("Hello World")  
  2. dofile 'a.lua'        <==>   dofile('a.lua')  
  3. print[[a multi-line   <==>   print([[a multi-line  
  4.  message]]                    message]])  
  5. f{x=10, y=20}         <==>   f({x=10, y=20})  
  6. type{}                <==>   type({})  

Lua支援面向物件,操作符為冒號‘:’。o:foo(x) <==> o.foo(o, x),這個在後面會專門寫一篇文章。

Lua程式可以呼叫C語言或者Lua實現的函式。Lua基礎庫中的所有函式都是用C實現的。呼叫一個用C實現的函式,和呼叫一個用Lua實現的函式,二者沒有任何區別。

Lua函式的定義語法比較常規,如下示例:

  1. function add(a)  
  2.     local sum = 0  
  3.     for i, v in ipairs(a) do  
  4.         sum = sum + v  
  5.     end  
  6.     return sum  
  7. end  

函式的引數跟區域性變數一樣,用傳入的實參來初始化,多餘的實參被丟棄,多餘的形參初始化為nil。示例如下:

  1. function f(a, b) return a or b end  
  2. f(3)        -- a=3, b=nil  
  3. f(3, 4)     -- a=3, b=4  
  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種呼叫場景,如果函式呼叫作為其最後一個表示式,那麼會保留所有的返回值,然後根據實際呼叫需要再糾正。

示例:

  1. -- 多賦值,函式呼叫是最後一個表示式  
  2. x,y = foo2()      -- x="a", y="b"  
  3. x = foo2()        -- x="a", "b" is discarded  
  4. x,y,z = 10,foo2() -- x=10, y="a", z="b"  
  5. x,y = foo0()      -- x=nil, y=nil  
  6. x,y = foo1()      -- x="a", y=nil  
  7. x,y,z = foo2()    -- x="a", y="b", z=nil  
  8. -- 多賦值,函式呼叫不是最後一個表示式,因此返回值只保留第一個  
  9. x,y = foo2(), 20     -- x="a", y=20  
  10. x,y = foo0(), 20, 30 -- x=nil, y=20, 30 is discarded  
  11. -- 返回值作為實參來呼叫其他函式  
  12. print(foo0())         -->  
  13. print(foo1())         --> a  
  14. print(foo2())         --> a b  
  15. print(foo2(), 1)      --> a 1  
  16. print(1, foo2())      --> 1 a b   
  17. print(foo2() .. "x")  --> ax (see next)  
  18. -- table中  
  19. t = {foo0()} -- t = {} (an empty table)  
  20. t = {foo1()} -- t = {"a"}  
  21. t = {foo2()} -- t = {"a", "b"}  
  22. -- table中,但是函式呼叫不是最後一個表示式  
  23. t = {foo0(), foo2(), 4} -- t[1] = nil, t[2] = "a", t[3] = 4  
  24. -- return語句中  
  25. function foo (i)  
  26.     if i == 0 then return foo0()  
  27.     elseif i == 1 then return foo1()  
  28.     elseif i == 2 then return foo2()  
  29.     end  
  30. end  
  31. print(foo(1)) --> a  
  32. print(foo(2)) --> a b  
  33. print(foo(0)) -- (no results)  
  34. print(foo(3)) -- (no results)  

用括號來強制返回值個數為一個:

  1. print((foo0())) --> nil  
  2. print((foo1())) --> a  
  3. print((foo2())) --> a  

因此,括號千萬別亂用,尤其是return後的值,如果用了括號,那麼就只返回一個值。

函式unpack可以返回多個值,它傳入一個array,然後返回array中的每一個值。

  1. print(unpack{10,20,30}) --> 10 20 30  
  2. a,b = unpack{10,20,30} -- a=10, b=20, 30 is discarded  

unpack的一個重要用法是泛型呼叫,提供了比C語言中更大的靈活性。在Lua中,如果你想呼叫一個函式f,傳入可變數量的引數,很簡單,

  1. f(unpack(a))  

unpack返回a中的所有值,並傳給作為引數,下面示例:

  1. f = string.find  
  2. a = {"hello", "ll"}  
  3. print(f(unpack(a)))  --> 3 4  

Lua中的unpack是用C實現的。其實我們也可以用Lua來實現它

  1. function unpack (t, i)  
  2.     i = i or 1  
  3.     if t[i] then  
  4.         return t[i], unpack(t, i + 1)  
  5.     end  
  6. end  

2. 變參

Lua中的一些函式接受可變數量的引數,例如print函式。print函式是用C來實現的,但是我們也可以用Lua來實現變參函式。下面是一個示例:

  1. function add (...)  
  2.     local s = 0  
  3.     for i, v in ipairs{...} do  
  4.         s = s + v  
  5.     end  
  6.     return s  
  7. end  
  8. print(add(3, 4, 10, 25, 12)) --> 54  

引數列表中的'...'指明該函式可以接受變參。我們可以將‘...’當作一個表示式,或者一個多返回值的函式(返回當前函式的所有引數)。例如

  1. local a, b = ...  

用可選引數的前兩個初始化區域性變數a,b的值。再看下面的例子,

  1. function foo(a, b, c)   <==>   function foo(...) local a, b, c = ...  
  1. function id (...) return ... end  

上面這個函式簡單地返回它所有的引數。下面的例子,說明了一個跟蹤函式呼叫的技巧

  1. function foo1 (...)  
  2.     print("calling foo:", ...)  
  3.     return foo(...)  
  4. end  

再看一個實用的例子。Lua提供了不同的函式來格式化文字string.formant和寫文字io.write,我們可以簡單地將二者合二為一。

  1. function fwrite (fmt, ...)  
  2.     return io.write(string.format(fmt, ...))  
  3. end  

注意有一個固定的引數fmt。變參函式可能含有不定數目的固定引數,後面再跟變參。Lua會將前面的實參賦值給這些固定引數,剩下的實參才能當作變參看待。下面是幾個示例:

  1. CALL                              PARAMETERS  
  2. fwrite()                       -- fmt = nil, no varargs  
  3. fwrite("a")                    -- fmt = "a", no varargs  
  4. fwrite("%d%d", 4, 5)           -- fmt = "%d%d", varargs = 4 and 5  

如果想要迭代處理變參,可以用{...}來將所有的變參收集到一個table中。但是有時變參中可能含有非法的nil,我們可以用select函式。select函式有一個固定的引數selector,然後跟一系列的變參。呼叫的時候,如果selector的值為數字n,那麼select函式返回變參中的第n個引數,否則selector的值為'#',select函式會返回可變引數的總數目。下面示例:

  1. for i=1, select('#', ...) do  
  2.     local arg = select(i, ...)     -- get i-th parameter  
  3.     <loop body>  
  4. end  

注意,select("#", ...)返回變參的數目,包括nil在內。

3. 帶名字的引數

Lua中函式的引數傳遞是基於位置的,當呼叫函式的時候,實參根據位置來匹配形參。但是,有的時候,根據名字來匹配更實用。例如,系統函式os.rename,我們會經常忘記新名字和舊名字哪個在前;為了解決這個問題,我們嘗試重新定義這個函式。下面這個

  1. -- invalid code  
  2. rename(old="temp.lua", new="temp1.lua")  

上面這個程式碼是非法的,Lua並不支援這樣的語法。但是我們可以修改一點點,來實現相同的效果。

  1. function rename (arg)  
  2.     return os.rename(arg.old, arg.new)  
  3. end  

用這種方式來傳遞引數是很實用的,尤其是,當函式有多個引數,並且其中一些是可有可無時。例如,用GUI庫建立一個新的視窗

  1. w = Window{ x=0, y=0, width=300, height=200,  
  2.             title = "Lua", background="blue",  
  3.             border = true  
  4.           }  

Window函式可以檢查必須的引數,並且給可選引數賦予預設值等。假設_Window函式可以用來建立一個新視窗,但是它必須要全部的引數。那我們就可以重新定義一個Window函式如下:

  1. function Window (options)  
  2.     -- check mandatory options  
  3.     if type(options.title) ~= "string" then  
  4.         error("no title")  
  5.     elseif type(options.width) ~= "number" then  
  6.         error("no width")  
  7.     elseif type(options.height) ~= "number" then  
  8.         error("no height")  
  9.     end  
  10.     -- everything else is optional  
  11.     _Window(options.title,  
  12.         options.x or 0,                     -- default value  
  13.         options.y or 0,                     -- default value  
  14.         options.width, options.height,  
  15.         options.background or "white",      -- default  
  16.         options.border                      -- default is false (nil)  
  17.         )  
  18. end