《lua程式設計》讀書筆記 第六章 深入函式

2021-08-01 21:22:37 字數 3267 閱讀 6693

在lua中,函式是一種「第一類值」,它們具有特定的詞法域。「詞法域」是什麼意思呢?這是指乙個函式可以巢狀在另乙個函式中,內部的函式可以訪問外部函式中的變數。

在lua中有乙個容易混淆的概念是,函式與所有其他值一樣是匿名的,即它們都沒有名稱。當討論乙個函式名時,實際上是在討論乙個持有某函式的變數。函式定義實際上是乙個表示式:

function foo(x) return 2*x end其實際是foo = function(x) return 2*x end, 其實其是一條賦值語句。

可以將表示式「function(x) < body > end」視為乙個函式的構造式,將構造式的結果稱為乙個匿名函式。

乙個函式可以作為另乙個函式的引數,接受另乙個函式作為實參的函式,稱為高階函式。

network = 

}table.sort(network, function

(a, b)

return (a.name > b.name) end) -->傳入乙個匿名函式為引數

若將乙個函式寫在另乙個函式內,那麼這個位於內部的函式可以訪問外部函式中的區域性變數,這項特徵被稱為「詞法域」。為什麼在lua中允許這種訪問呢?原因在於函式是「第一類值」。看如下**:

function

newcounter

() local i = 0

return

function

() i = i + 1

return i

endendlocal c1= newcounter()

c1() -->1

c1() -->2

簡單來講,乙個closure就是乙個函式加上這個函式訪問的所有「非區域性變數」。如果再次呼叫newcounter,那麼它會建立乙個新的closure。

local c2 = newcounter()

print(c2()) -->1

print(c1()) -->3

從技術上講,lua中只有closure而沒有函式,函式只是一種特殊的closure。

closure還有一些常用的用法,比如重定義乙個預設的函式:

oldsin = math.sin

math.sin = function

(x) return oldsin(x*math.pi . 180)

end

可以使用同樣的技術建立乙個安全的執行環境,即所謂的「沙盒」。

do

local oldopen = io.open

local access_ok = function(filename, mode)

< 檢查一些訪問許可權 >

endio.open = function(filename, mode)

if access_ok(filename, mode) then

return oldopen(filename, mode)

else

return nil, "access denied"

end end

end

在lua中,函式通常儲存在乙個table中,而不是作為全域性變數使用:

lib1 = {}

lib1.foo = function

(x, y)

return x + y end

lib2 =

除此之外,lua還提供了另一種語法來定義這類函式:

lib = {}

function

lib.foo

(x,y)

return x+y end

對於這種區域性函式的定義,lua還支援一種特殊的「語法糖」

local

function

foo(< 引數 >)

《函式體》

end

另外在定義遞迴的區域性函式有一點要特別注意。看以下**:

local fact = function(n)

if n == 0

then

return

1else

return n*fact(n-1)

endend

這樣定義的錯誤的,關鍵在於其先計算右邊的表示式,然後再定義左邊變數,而在呼叫fact(n-1)時,fact變數尚未定義完畢,因此其實際會呼叫全域性的fact,而非其本身。

正確的寫法如下,應先定義區域性變數fact:

local fact

fact = function(n)

if n==0

then

return

1else

return n * fact(n -1)

endend

而對於local function foo《引數》 《函式體》 end的定義方式,lua將其展開為

local foo

foo = function

(《引數》)

《函式體》 end

所以使用這種方式定義遞迴函式不會有錯誤。

當然,這個技巧對於間接遞迴的函式是無效的,你必須使用乙個明確的前置宣告。

local f,g

functiong()

f()end

functionf()

-->注意不要用local function,否則其會建立乙個f覆蓋前面的變數

g()end

lua支援「尾呼叫消除」。所謂「尾呼叫」就是一種類似於goto的函式呼叫。當乙個函式呼叫時另乙個函式的最後乙個動作時,該呼叫才算是一條「尾呼叫」。

function

f(x)

return g(x) end

在上述呼叫中,當f呼叫完g後便無事可做了,因此程式不在需要保留函式f的堆疊資訊,所以在進行尾呼叫時不耗費任何棧空間,這種實現便是「尾呼叫消除」。由於「尾呼叫」不會消耗棧空間,所以乙個程式可以擁有無數巢狀的「尾呼叫」:

function

foo(n)

if n > 0

then

return foo(n-1) end

end

在lua中「尾呼叫」的一大應用就是編寫「狀態機」。這種程式以乙個函式來表示乙個狀態。

第六章讀書筆記

linux系統將每個驅動都對映成乙個檔案,這些檔案稱為裝置檔案或驅動檔案,都儲存在 dev目錄中。編寫linux驅動程式的步驟 第1 步 建立linu x 驅動骨架 裝載和解除安裝linu x 驅動 第2 步 註冊和登出裝置檔案 第3 步z 指定與驅動相關的資訊 第4 步 指定 函式 第5 步z 編...

第六章 讀書筆記

第六章主要講的是第乙個linux 驅動程式 統計單詞個數。從這章開始進入了實戰階段,這一章首先介紹了linux 驅動的工作方式,linux 將每乙個驅動都對映成乙個檔案,這些檔案被稱為裝置檔案或驅動檔案,都儲存在 dev 目錄中,使得 linux 驅動互動就像是普通檔案互動一樣。編寫linux 驅動...

C Primer 讀書筆記 第六章

第 章 語句 簡單語句 表示式語句 expression r statement 乙個表示式加上結尾的分號,執行時導致該表示式被求值 空語句 null statement 只由乙個單獨的分號組成,當語法上需要乙個語句但邏輯上並不需要時使用 宣告語句 用於宣告或定義物件或類 復合語句 復合語句 com...