前言
Lua中的函數(shù)和C++中的函數(shù)的含義是一致的,Lua中的函數(shù)格式如下:
復(fù)制代碼 代碼如下:
function MyFunc(param)
-- Do something
end
在調(diào)用函數(shù)時,也需要將對應(yīng)的參數(shù)放在一對圓括號中,即使調(diào)用函數(shù)時沒有參數(shù),也必須寫出一對空括號。對于這個規(guī)則只有一種特殊的例外情況:一個函數(shù)若只有一個參數(shù),并且此參數(shù)是一個字符串或table構(gòu)造式,那么圓括號便可以省略掉??匆韵麓a:
復(fù)制代碼 代碼如下:
print "Hello World" --> print("Hello World")等價
print [[a multi-line
message]] -->print([[a multi-line
--> message]]) 等價
-- f是一個函數(shù)
f{x=10, y=20} -->f({x=10, y=20}) 等價
上面代碼的一些簡便寫法,如果不熟悉的話,在閱讀別人的代碼時,就會是一頭霧水。
一個函數(shù)定義具有一個名稱、一系列的參數(shù)和一個函數(shù)體。函數(shù)定義時,所定義的參數(shù)的使用方式與局部變量非常相似,它們是由調(diào)用函數(shù)時的“實際參數(shù)”初始化的。調(diào)用函數(shù)時提供的實參數(shù)量可以與形參數(shù)量不同。Lua會自動調(diào)整實參的數(shù)量,以匹配參數(shù)表的要求,若“實參多余形參,則舍棄多余的實參;若實參不足,則多余的形參初始化為nil”。這個與接下來要介紹的多重返回值非常相似。
多重返回值
這個應(yīng)該是Lua的一個特征吧。允許函數(shù)返回多個結(jié)果,只需要在return關(guān)鍵字后列出所有的返回值即可。以下根據(jù)帶來來說明情況:
復(fù)制代碼 代碼如下:
function foo0() end -- 無返回值
function foo1() return "a" end -- 返回一個結(jié)果
function foo2() return "a", "b" end -- 返回兩個結(jié)果
-- 在多重賦值時,如果一個函數(shù)調(diào)用是最后,或僅有的一個表達(dá)式,
-- 那么Lua會保留其盡可能多的返回值,用于匹配賦值變量
x, y = foo2() -- x = "a", y = "b"
x = foo2() -- x = "a", "b"被丟棄
x, y, z = 10, foo2() -- x = 10, y = "a", z = "b"
-- 如果一個函數(shù)沒有返回值或者沒有足夠多的返回值,那么Lua會用
-- nil來補充缺失的值
x, y = foo0() -- x = nil, y = nil
x, y = foo1() -- x = "a", y = nil
x, y, z = foo2() -- x = "a", y = "b", z = nil
-- 如果一個函數(shù)調(diào)用不是一系列表達(dá)式的最后一個元素,那么將只產(chǎn)生一個值:
x, y = foo2(), 20 -- x = "a", y = 20
x, y = foo0(), 20, 30 -- x = nil, y = 20, 30則被丟棄
-- table構(gòu)造式可以完整的接收一個函數(shù)調(diào)用的所有結(jié)果,即不會有任何數(shù)量
-- 方面的調(diào)整
local t = {foo0()} -- t = {}(一個空的table)
local t = {foo1()} -- t = {"a"}
local t = {foo2()} -- t = {"a", "b"}
-- 但是,對于上述的行為,只有當(dāng)一個函數(shù)調(diào)用作為最后一個元素時才會發(fā)生,
-- 而在其他位置上的函數(shù)調(diào)用總是只產(chǎn)生一個結(jié)果值
local t = {foo0(), foo2(), 4} -- t[1] = nil, t[2] = "a", t[3] = 4
-- 我們也可以在一個函數(shù)中,使用return返回另一個函數(shù)
function MyFunc() -- 返回a
return foo1() -- 注:這里是return foo1(),而不是return (foo1())
end
-- return foo1()和return (foo1())是兩個完全不同的意思
-- 將一個函數(shù)調(diào)用放入一對圓括號中,從而迫使它只返回一個結(jié)果
print((foo0())) -- nil
print((foo1())) -- a
print((foo2())) -- a
變長參數(shù)
在C語言中,函數(shù)可以接受不同數(shù)量的實參,Lua中的函數(shù)也可以接受不同數(shù)量的實參,例如以下代碼:
復(fù)制代碼 代碼如下:
-- 打印所有的參數(shù)
function VarArguments(...)
for i, v in ipairs{...} do
print(v)
end
end
VarArguments(1, 2, 3)
參數(shù)表中的3個點(…)表示該函數(shù)可接受不同數(shù)量的實參。當(dāng)這個函數(shù)被調(diào)用時,它的所有參數(shù)都會被收集到一起。這部分收集起來的實參稱為這個函數(shù)的“變長參數(shù)”。一個函數(shù)要訪問它的變長參數(shù)時,仍需要用到3個點(…)。但不同的是,此時這3個點是作為一個表達(dá)式來使用的。在上例中,表達(dá)式{…}表示一個由所有變長參數(shù)構(gòu)成的數(shù)組。在C語言中使用變長參數(shù)需要注意的問題,在Lua中同樣需要注意。
通常一個函數(shù)在遍歷其變長參數(shù)時只需要使用表達(dá)式{…},這就像訪問一個table一樣,訪問所有的變長參數(shù)。然而在某些特殊的情況下,變長參數(shù)中可能會包含一些故意傳入的nil,那么此時就需要用select來訪問變長參數(shù)了。調(diào)用select時,必須傳入一個固定實參selector和一系列變長參數(shù)。如果selector為數(shù)字n,那么select返回它的第n個可變實參;否則selector只能為字符串“#”,這樣select會返回變長參數(shù)的總數(shù),請看以下代碼:
復(fù)制代碼 代碼如下:
for i = 1, select('#', ...) do
local arg = select(i, ...) -- 得到第i個參數(shù)
-- Do something else
end
select(‘#', …)會返回所有變長參數(shù)的總數(shù),其中包括nil(還記得table.maxn么?)對于Lua 5.0版本來說,變長參數(shù)則有另外一套機制。聲明函數(shù)的語法是一樣的,也是將3個點作為最后一個參數(shù)。但Lua 5.0沒有提供“…”表達(dá)式。而是通過一個隱含的局部table變量“arg”來接受所有的變長參數(shù)。這個table還有一個名為“n”的字段,用來記錄變長參數(shù)的總數(shù),例如以下代碼:
復(fù)制代碼 代碼如下:
function MyFunc(a, b, ...)
print(arg.n)
end
MyFunc(1, 2, 3, 4, 5) -->3
這套舊機制的缺點在于,每當(dāng)程序調(diào)用了一個具有變長參數(shù)的函數(shù)時,都會創(chuàng)建一個新的table。而在新機制中,只有在需要時才會去創(chuàng)建這個用于變長參數(shù)訪問的table。這里只是對這個方法進(jìn)行簡單介紹,別在閱讀別人的代碼時,看不懂?。。?/p>
深入討論函數(shù)
在Lua中,函數(shù)與其它傳統(tǒng)類型的值具有相同的權(quán)利。函數(shù)可以存儲到變量或table中,也可以作為實參傳遞給其它函數(shù),還可以作為其它函數(shù)的返回值。在Lua中有一個容易混淆的概念是,函數(shù)與所有其它值一樣都是匿名的,即它們都沒有名稱。當(dāng)討論一個函數(shù)名時,實際上是在討論一個持有某函數(shù)的變量,例如以下代碼:
復(fù)制代碼 代碼如下:
-- 我們經(jīng)常這樣定義函數(shù)
function foo(x) return 2 * x end
-- 實際上,這只是一種“語法糖”而已;
-- 上述代碼只是下面代碼的一種簡化書寫形式
foo = function (x) return 2 * x end
實際上,一個函數(shù)定義實際就是一條語句(更準(zhǔn)確地說是一條賦值語句),這條語句創(chuàng)建了一種類型為“函數(shù)”的值,并將這個值賦予一個變量。由于函數(shù)在Lua中就是一個普通的值,所以不僅可以將其存儲在全局變量中,還可以存儲在局部變量甚至table的字段中。
內(nèi)嵌函數(shù)
若將一個函數(shù)寫在另一個函數(shù)之內(nèi),那么這個位于內(nèi)部的函數(shù)便可以訪問外部函數(shù)中的局部變量,這個特征叫做“詞法域”。我們來看看下面一段有趣的代碼:
復(fù)制代碼 代碼如下:
function newCounter()
local i = 0
return function () -- 匿名函數(shù)
i = i + 1
return i
end
end
c1 = newCounter()
print(c1()) -->輸出什么?
print(c1()) -->又輸出什么?
如果你很明白上面的輸出,很明白上面的代碼,那么閉合函數(shù)這一小節(jié)就不需要閱讀了。在上述代碼中,有一個變量i,對于函數(shù)newCounter來說,i是一個局部變量,但是對于匿名函數(shù)來說,當(dāng)它訪問這個i時,i既不是全局變量,也不是局部變量,對于我們來說,我們稱這樣的變量為一個“非局部的變量”。下面這段代碼也是同樣的道理:
復(fù)制代碼 代碼如下:
function newCounter(i)
return function () -- 匿名函數(shù)
i = i + 1
return i
end
end
c1 = newCounter(10)
print(c1()) -->輸出什么?
print(c1()) -->又輸出什么?
匿名函數(shù)訪問了一個“非局部的變量”i,該變量用于保持一個計數(shù)器。乍一看,由于創(chuàng)建變量i的函數(shù),也就是newCounter已經(jīng)返回,所以之后每次調(diào)用匿名函數(shù)時,i都應(yīng)該是已經(jīng)超出了作用范圍。但是,Lua會以closure的概念來正確地處理這種情況。在這里簡單的講,一個closure就是一個函數(shù)加上該函數(shù)所需訪問的所有“非局部的變量”。如果再次調(diào)用newCounter,那么它會創(chuàng)建一個新的局部變量i,從而將得到一個新的closure。在后續(xù)的總結(jié)中,我會專門總結(jié)一篇關(guān)于Lua中的閉包的博文,敬請期待。
非全局的函數(shù)
由于函數(shù)和普通變量一樣,所以函數(shù)不僅可以存儲在全局變量中,還可以存儲在table的字段中,或局部變量中。我們可以把函數(shù)存在一個table中,比如以下代碼:
復(fù)制代碼 代碼如下:
Lib = {}
Lib.foo = function (x, y) return x + y end
Lib.goo = function (x, y) return x - y end
只要將一個函數(shù)存儲在一個局部變量中,就得到了一個“局部函數(shù)”,也就是說這個函數(shù)只能在某個特定的作用域內(nèi)才有效。我們可以這樣定義一個局部的函數(shù):
復(fù)制代碼 代碼如下:
local f = function (參數(shù)>)
函數(shù)體>
end
-- Lua還提供另一種特殊的“語法糖”
local function f (參數(shù)>)
函數(shù)體>
end
有的時候,我們需要進(jìn)行函數(shù)的前置聲明,比如以下代碼:
復(fù)制代碼 代碼如下:
local f, g
function f()
一些其它操作>
g()
end
function g()
一些其它操作>
f()
end
總結(jié)
這篇博文對Lua中的函數(shù)進(jìn)行了大體上的總結(jié),至少看完這篇博文,你會使用Lua寫函數(shù)了,會使用Lua中的函數(shù)了。但是對于比較深的東西,這里沒有總結(jié),比如“閉包”。我會專門寫一篇關(guān)于Lua中的閉包的文章。
您可能感興趣的文章:- Lua中的string庫(字符串函數(shù)庫)總結(jié)
- Lua中的函數(shù)(function)、可變參數(shù)、局部函數(shù)、尾遞歸優(yōu)化等實例講解
- Lua中的一些常用函數(shù)庫實例講解
- Lua中的模塊與module函數(shù)詳解
- Lua字符串庫中的幾個重點函數(shù)介紹
- Lua的table庫函數(shù)insert、remove、concat、sort詳細(xì)介紹
- Lua中的常用函數(shù)庫匯總
- Lua中的面向?qū)ο缶幊淘斀?/li>
- Lua面向?qū)ο笾惡屠^承
- Lua面向?qū)ο笾嘀乩^承、私密性詳解
- Lua面向?qū)ο缶幊虒W(xué)習(xí)筆記
- Lua中函數(shù)與面向?qū)ο缶幊痰幕A(chǔ)知識整理