Function Description
在LUA中, Function和Number, String一樣屬于基本類型. 凡是基本類型可以出現的地方Function一樣可以出現. 這意味著Function可以存儲在變量中,可以作為另一個Function的參數傳遞,也可以作為某個Function的返回值. 可以把Function德名字視為一種變量,它指向具體的Function實現. 這樣的機制給程序設計帶來了極大的彈性. 一個程序可以重寫某個Function以便給他增加功能, 或者刪除某個函數創建安全運行環境(SandBox).
a = {p = print}
a.p("Hello World") --> Hello World
print = math.sin -- `print' now refers to the sine function
a.p(print(1)) --> 0.841470
sin = a.p -- `sin' now refers to the print function
sin(10, 20) --> 10 20
function foo (x) return 2*x end 等價于 foo = function (x) return 2*x end
在調用LUA的Function時,所有的參數應該包圍在'('和')'之間, 即使函數沒有參數, '('和')'也不應該被省略.
print(8*9, 9/8)
a = math.sin(3) + math.cos(10)
print(os.date())
這兒有一種例外情況, 如果Function只有一個參數且這個參數是literal string或者table constructor, 這時大括號keyi省略.
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 作為額外的參數放到第一個參數的位置.
Function Definition
LUA中的函數定義和許多程序設計語言一樣, 需要有一個函數名, 一系列的參數和函數體.
-- add all elements of array `a'
function add (a)
local sum = 0
for i,v in ipairs(a) do
sum = sum + v
end
return sum
end
函數的參數是局部變量, LUA中比較特殊的是調用函數時參數的個數可以和定義時不一樣. LUA會調整參數的個數, 這一點很像多重賦值, 調用函數時所給的多余參數會被丟掉, 調用函數時如果參數不夠后邊的會被賦為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被拋棄)
這種機制和大多數強類型編成語言(C,C++,Java)不同, 不過這種做法帶來了一些好處:
function incCount (n)
n = n or 1
count = count + n
end
這里1作為函數的默認參數, 如果調用時不給參數, 那么n的值就是nil, 否則就是用用戶調用時給的值.
Multiple Results
多重返回值是LUA提供的一種并不符合傳統, 但是十分方便的機制. 一些LUA預定義的函數也使用了多重返回值的機制, 例如 string.find 函數, 他的兩個返回值分別表示查找到的字符串的起始和結束位置. 如果沒找到返回值為nil.
s, e = string.find("hello Lua users", "Lua")
print(s, e) --> 7 9
使用多重返回值的方法很簡單, 你只需要將返回的變量列舉出來就可以了. 以下的例子函數返回一個數組中的最大值和最大值元素的索引.
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
Variable Number of Arguments
在LUA中Function支持不定個數的參數, 用 ... 表示. 這和LUA中普通函數的調用是有所區別的. 調用函數時所給參數可以和定義時不同, 但這里其實是LUA編譯器對參數作了自動補足, 調用時參數不夠的話多余的置為nil, 調用時參數過多則將多余的拋棄. 并不是真正的參數個數不定.
printResult = ""
function print (...)
for i,v in ipairs(arg) do
printResult = printResult .. tostring(v) .. "\t"
end
printResult = printResult .. "\n"
end
這里...表示參數個數不確定, 當print調用時, 所有的參數都被存儲在一個table內, 這個隱藏變量的名字叫arg, 除了函數的參數外, arg還含有一個額外的元素n, 用來表示參數的個數.
一個Function可以有多個返回值, 有時我們想指定使用返回值中的某一個而忽略其他的, 這時可以用 _ 表示忽略.
local _, x = string.find(s, p)
-- now use `x'
...
另一種方法是使用select函數,
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
對于以下情況:
function g (a, b, ...) end
調用結果如下:
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}
Named Arguments
在LUA中, 如果一個函數有許多參數而且大多數參數都是可選的話, 將參數定義為table會帶來一些額外的方便性, 你不需要記住參數的位置, 只需要記住參數的名字即可.
例如一個產生窗口的函數可能有許多參數,
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
調用時很簡單, 你只需要給出參數的名字和值, 順序無關緊要.
w = Window{ x=0, y=0 }
Closure
Function屬于基本類型, 所以一個Function可以返回另一個Function.
function foo()
return function() return end
end
c1 = foo() --> 每次調用返回一個匿名的function對象.
c2 = foo()
print(c1) --> function: 0x87dd470
print(c2) --> function: 0x87dd490
c1和c2是不同的對象.
同樣, 如果一個作為返回值的函數對象內使用了上層的局部變量, 每一個返回的函數對象內使用的這個局部變量都是不同的. 這種情況稱作closure.
function foo()
local x=10
return function()
x=x+1
return x end
end
c1 = foo() --> 每次調用返回一個匿名的function對象.
c2 = foo()
print(c1) --> function: 0x87dd470
print(c2) --> function: 0x87dd490
print(c1()) --> 11
print(c1()) --> 12
print(c2()) --> 11
print(c1()) --> 13
print(c2()) --> 12
Proper Tail Calls
Proper Tail Calls是LUA的另一個有趣的特性, 在一個LUA函數中, 如果最后一個操作是返回一個函數調用, 例如 return g(...), 那么LUA不會把它當作一個函數調用而建立調用堆棧而只簡單的跳轉到另一個函數中.
function foo (n)
if n > 0 then return foo(n - 1) end
end
這個函數無論n是多大都不會發生堆棧溢出, 這里return foo(...)的功能相當于goto.
以下調用并不是Tail Calls, 因為函數調用返回后還作了其他的操作:
return g(x) + 1 -- must do the addition
return x or g(x) -- must adjust to 1 result
return (g(x)) -- must adjust to 1 result
在LUA中, 只有形如return g(...)是tail call. 然而g和他的參數可以是復雜的調用形式, 因為LUA會先計算表達式的值然后調用函數, 所以下面是一個tail call.
return x[i].foo(x[j] + a*b, i + j)