在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...