Lua學習筆記 第六章 深入函式
在Lua中,函式是一種第一類值,它們具有特定的詞法域;
第一類值表示函式與其他傳統型別的值具有相同的權利,函式可以儲存到變數中或table中,
可以作為實參傳遞給其他函式,還可以作為其他函式的返回值;
詞法域是指一個函式可以巢狀在另一個函式中,內部的函式可以訪問外部函式中的變數;
在Lua中函式與所有其它值一樣都是匿名的,即它們都沒有名稱。當討論一個函式名時實際
上是討論一個持有某函式的變數;
Lua中最常見的函式編寫方式,如:function foo(x) return2*x end
只是所謂的"語法糖"而已。也就是以下程式碼的一種簡化書寫形式:
foo = function (x) return 2*x end
因此一個函式定義實際上就是一條賦值語句,這條語句建立了一種型別為"函式"的值,並將這
個值賦予一個變數;
table庫提供的一個函式table.sort, 它接受一個table並對其中的元素排序。
table.sort(network, function(a, b)
return(a.name > b.name)
end)
其中第二個引數就是匿名引數;
像sort這樣的函式,接受另一個函式作為實參的,稱其是一個"高階函式",高階函式是一種強大的程式設計機制;
6.1閉合函式(closure)
若把一個函式寫在另一個函式之內,那麼這個位於內部的函式便可以訪問到外部函式中的區域性變數。
這項特徵稱之為"詞法域"!對於內部函式而言,外部函式的區域性變數稱為"非區域性的變數";
而一個closure就是一個函式加上該函式所需訪問的所有"非區域性的變數";因此函式本身可以認為是一種特殊的closure;
閉合函式的例子:
do
local oldOpen =io.open
localaccess_OK = function(filename, mode)
<檢查訪問許可權>
end
io.open =function(filename, mode)
if access_OK(filename,mode) then
returnoldOpen(filename, mode)
else
returnnil, "access denied"
end
end
end
6.2非全域性的函式(non-global function)
由於函式是第一類值,因此函式不僅可以儲存在全域性變數中,還可以儲存到table的
欄位中和區域性變數中;在table儲存函式的幾種寫法:
Lib = {}
Lib.foo = function (x, y) return x + y end
Lib.goo = function (x, y) return x - y end
Lib = {
foo =function (x, y) return x + y end
goo =function (x, y) return x - y end
}
Lib = {}
function Lib.foo (x, y) return x + y end
function Lib.goo (x, y) return x -y end
若將一個函式儲存到區域性變數中,即得到了一個區域性函式,該函式只能在某個特定的作用域中使用;
Lua是將每個程式塊(chunk)作為一個函式來處理的,所以在一個程式塊中宣告的函式也是區域性函式;
Lua中區域性函式的定義的基本語法:
local f = function (<引數>)
<函式體>
end
對於這種區域性函式的定義,Lua還支援一種特殊的"語法糖":
local function f (<引數>)
<函式體>
end
當Lua展開定義區域性函式的"語法糖"時,並不是使用定義函式的基本語法;如對於區域性函式的定義:
local function foo (<引數>) <函式體> end
Lua將其展開為:
local foo
foo = function (<引數>) <函式體> end
所以在Lua中定義區域性的遞迴函式有兩種寫法:
local fact
fact = function(n)
if n== 0 thenreturn 1
else returnn*fact(n-1)
end
end
local function fact(n)
if n==0 thenreturn 1
else returnn*fact(n-1)
end
end -- 經Lua展開後與第一種寫法一樣
6.3正確的尾呼叫
Lua支援尾呼叫消除(tail-call-elimination);
所謂"尾呼叫(tail call)"就是一種類似於goto的函式呼叫.當一個函式呼叫是另一個函式的最後一個動作時,
該呼叫才算是一條尾呼叫。如:
function f(x) return g(x) end
也就是說f呼叫完g之後就再無其它的事情可做了;因此程式就不需要返回那個尾呼叫所在的函式。
在尾呼叫之後,程式也不需要儲存任何關於該函式的棧資訊。當g返回時,執行的控制權可以直接
返回到呼叫f的那個點上。
在進行尾呼叫時不會消耗任何棧空間,這種實現成為支援"尾呼叫消除"。
由於尾呼叫不會耗費棧空間,所以一個程式可以擁有無數巢狀的"尾呼叫".如:
function foo(n)
if n > 0then return foo(n-1) end
end
一條尾呼叫就好比是一條goto語句.因此Lua中尾呼叫的一大應用就是編寫"狀態機"。
這種程式通常以一個函式表示一個狀態,改變狀態就是goto(呼叫)到另一個特定的函式.
例子見P53(迷宮遊戲)