1. 程式人生 > >Lua學習筆記 第六章 深入函式

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(迷宮遊戲)