lua 函式深入 閉合函式,區域性函式,尾呼叫

2021-07-03 06:33:50 字數 4290 閱讀 8534

lua函式具有兩大特徵:函式作為第一類值,函式具有特定的詞法域(lexical scoping)

所謂第一類值:代表函式和其他傳統型別的值是等價的(例如數字和字串),函式可以同他們一樣儲存在變數,table中,可以作為實參傳遞,可以作為函式返回值。

對於第一類值,需要講明,函式和其他值一樣都是匿名的,是沒有名字的。而我們平時所說的函式名,如print(),都只是一種語法糖,乙個持有某個函式的變數。

例如:function foo(x) return 2*x end                           --->等價於                                  foo = function(x)   return 2*x end     

函式定義實際上是一條賦值語句。這條語句先建立乙個型別為函式的值,然後將其賦值給乙個變數。

我們將表示式「function (x) end」視為函式的構造式,又被稱其為匿名函式,在某些特殊情況下,我們會用到匿名函式。

特定的詞法域(lexical scoping):指乙個函式可以巢狀在另乙個函式中,內部的函式可以訪問外部函式的變數。

閉合函式(closure)

由於詞法域的原因,當乙個函式寫在另乙個函式內部,而這個內部函式可以訪問外部函式的區域性變數,這個被訪問的區域性變數既不是全域性的也不是區域性的,被稱為非區域性的變數(non-local variable).

所以閉合函式就是這個內部函式加上該函式需要訪問的所有的非區域性變數的函式。

關於閉包的實現原理,推薦以下部落格

關於閉包,有幾個需要注意的地方:

e.g.

function newcounter()

local i = 0;

return function()

i = i+1

return i

endendc = newcounter() -- newcounter返回匿名函式賦予變數c,此時c是乙個閉包,同時擁有乙個upvalue值

print(c()) -->1

print(c()) -->2 --匿名函式再次訪問閉包中的upvalue值,使其加1.

--注意,如果再次呼叫newcounter,它將建立乙個新的閉包,該閉包將擁有乙個新的upvalue.

c1 = newcounter() -- 返回新的閉包,賦值給變數c1.

--同時需要注意:如果乙個函式內部,有多個閉合函式,這些閉合函式如果需要訪問同乙個外部變數,那麼這個upvalue在這些閉包之間是共享的。、

--e.g.

function f()

local i = 0

local function foo1()

i = i +1

return print(i)

endlocal function foo2()

i = i+10

return print(i)

endreturn foo1, foo2

endt1,t2 = f() -- t1,t2閉包函式共享i這個upvalue值。

t1() -->1

t2() -->11

閉包有很多作用,主要體現在,當上一級函式的區域性變數被釋放後,還可以通過閉包函式的upvalue值獲的訪問。

非區域性的函式(non-local function)

函式不僅可以儲存在全域性變數中,還可以儲存在table欄位和區域性變數中。因為函式是第一類值

table欄位中:

lib = {}

lib.foo = function(x,y) return x+y end

lib.goo = function(x,y) return x-y end

還有這種方式:

lib =

和這種方式:

lib = {}

function lib.foo(x,y) return x+y end

function lib.goo(x,y) return x-y end

將函式儲存在區域性變數中,便得到乙個區域性函式。區域性函式只能在特定的作用域中使用。

lua將每個程式塊(chunk)作為乙個函式來處理,所以裡面宣告的函式就是區域性函式,只在該程式塊中可見。詞法域保證了程式包中的其他函式可以使用這些區域性函式

比如:local f = function ()

《函式體》

endlocal g = function()

《**》

f()                    --   f()在這裡可見

《**》

end對於區域性函式的定義,lua也提供一種語法糖:

loacal function f()

《函式體》

end注意,當lua在展開這種語法糖時,並不使用基本函式定義語法。而是;將其展開為以下這種:

local f

f = function() 《函式體》 end      --對比基本函式定義語法的不同:local f = function() 《函式體》 end

lua這樣做,是有用處的。

比如在定義遞迴的區域性函式時,如果採用了基本函式定義語法,大多是錯誤的:

local fact = function(n)

if n == 0 then return 1

else return n* fact(n-1)      -- 錯誤

endend

當lua編譯到fact(n-1)時,由於區域性變數fact尚未完成定義,所以這句表示式最終呼叫了乙個全域性的fact,而非此函式本身。

為了解決這個問題,可以先定義乙個區域性變數,然後再定義函式本身:

local fact

fact = function()

if n==0 then return 1

else return n*fact(n-1)

endend

現在,fact()的呼叫就表示了區域性變數,即使在定義時,這個區域性變數的值還沒有完成定義,但在執行時,fact已經擁有了正確的值。

當然解決方法,也可以用上面提到的語法糖,因為lua對其的展開並不是使用基本函式定義語法。

so:local function fact(n)

if n == 0 then return 1

else return n* fact(n-1)

endend

這種方式完全等價於上面。

這個技巧對於間接遞迴的函式是無效的,在這種情況中,必須使用明確的前向宣告(forward declaration)

local f, g        --明確的前向宣告

function g()

f()end

function f()

g()end

注意,別把第二個函式定義寫為「local function f」.那麼lua會建立乙個新的local f,而原來宣告的f將置於未定義狀態。

正確的尾呼叫(proper tail call)

lua還支援尾呼叫消除(tail-call elimination)這一特性。

尾呼叫:當乙個函式是另乙個函式的最後乙個動作時,該呼叫就是一條尾呼叫。

尾呼叫消除:當在進行尾呼叫時不消耗任何棧空間,這種實現就稱為尾呼叫消除。

e.g.

function f(x) return g(x) end

當程式執行完g(x)之後,程式就不需要回到f()這個函式了,因為已經無事情可做了,這時程式也不需要儲存任何關於f()函式的棧資訊了。當g返回,程式的控制權直接返回到呼叫f的那個點上。

簡單來說,就是程式在進入g(x)後,上一級函式的棧空間被完全釋放,從而節省了記憶體。

正是這個尾呼叫消除特性,使得乙個程式可以擁有無數巢狀的尾呼叫,而不會造成棧溢位。

function foo(n)

if n>0 then return foo(n-1) end

end當然要享受尾呼叫消除這個特性,就必須保證這個呼叫時尾呼叫。判斷的標準就是,在呼叫玩這個函式之後,上一級的函式就無事情可做了。

funtion f(x) g(x) end            -- 呼叫完g後,還需要返回f,丟棄g返回的臨時結果。

return  g(x) + 1        -- 還要做一次加法

return x or g(x)        -- 還要將其調整為乙個返回值

return (g(x))        -- 還要將其調整為乙個返回值

只有這種形式的呼叫才是正確的:return ()   .

由於一條尾呼叫就好比一條goto語句,所以尾呼叫的一大應用就是編寫狀態機(state machine)

Lua學習之閉合函式

閉合函式 closure 看lua程式設定這本書的時候,其實感覺也就lua也就那樣,直到看到閉合函式這乙個塊 首先來說說函式,函式在lua中屬於第一類值,其實對於函式而言,function a end 和 a function end 是等價的,乙個函式的定義實際上就是一條賦值語句,這條語句首先建立...

lua基礎學習 閉合函式(closure)

先看 newcounter function add local i 0 counter function i i add return i endreturn counter endc1 newcounter 1 print c1 print c1 do local oldcounter newc...

Lua函式式程式設計和區域性函式詳解

函式式程式設計中的函式這個術語不是指計算機中的函式 實際上是subroutine 而是指數學中的函式,即自變數的對映。也就是說乙個函式的值僅決定於函式引數的值,不依賴其他狀態。比如sqrt x 函式計算x的平方根,只要x不變,不論什麼時候呼叫,呼叫幾次,值都是不變的。在函式式語言中,函式作為一等公民...