Lua 設計模式 |
|
nil
(或 NAN=0/0
鍵)。...
。function() return ... end
更簡潔地表示匿名函式
傳統上,如果我們要定義一個變數或鍵值對的表格,我們會使用表格建構語法 {...}
-- traditional method. local squares = {}; for n=1,10 do squares[n] = n^2 end local v = { x = 50, squares = squares, hello = function() print("hello?") end }
不過,透過適當地定義函式 collect
,我們也可以交替地如下建構
local v = collect(function() if true then x = 50 end squares = {}; for n=1,10 do squares[n] = n^2 end function hello() return "hello?" end end)
請注意,一個潛在的好處是,程式碼敘述可以穿插並建構鍵值定義。雖然它確實會造成一些更多的開銷。
collect
的定義如下
function collect(f) -- This collects globals defined in the given function. local collector = setmetatable({}, {__index = _G}) -- Call function in collector environment setfenv(f, collector)() -- Extract collected variables. local result = {}; for k,v in pairs(collector) do result[k] = v end return result end
測試套件
assert(v.x == 50) assert(#v.squares == 10) assert(v.hello() == "hello?") assert(v.print == nil) -- ensure _G not included. print("done")
此類型的機制用於 Lua 5.1 模組系統,其中收集函式在檔案中給定,而 require
/module
函式實作收集機制(以及其他事項)。請參閱 [Programming in Lua 第二版] 的第 15 章。
module("mymodule") x = 50 color = "blue" function hello() return "hello?" end
--DavidManura,2006-10,Lua 5.1
有很多有趣的辦法可以在特定事件上執行一系列動作。我見過的一種效率較低的方法如下所示
for _,v in pairs(files_in_directory) do dofile(v) if action then action(args) end action = nil end而目錄中的檔案可能如下所示
function action(something) print(something) end這是低效率的;它要求每次呼叫都必須重新剖析,且會破壞名為「動作」的全局。它也沒提供有效加權。在 naim 中,我們使用了以 C 編寫的掛勾系統來建立一系列鏈,我們可以在其中註冊 C 和 Lua 動作。
我寫了一個系統,可以在 Lua 中建立自己的鉤子鏈,並可像執行函式那樣執行。語法對我來說似乎相當合理
require"hooks" myhooks = {} myhooks.test = hooks:new() myhooks.ref1 = myhooks.test:insert(function (foo, bar) print("Test 1", foo, bar) end, 100) myhooks.ref2 = myhooks.test:insert(function (foo, bar) print("Test 2", foo, bar) end, 50) myhooks.ref3 = myhooks.test:insert(function (foo, bar) print("Test 3", foo, bar) end, 150) print"--" myhooks.test("Hello", "world") myhooks.test:remove(myhooks.ref1) print"--" myhooks.test("Hello", "world")
執行後會產生以下輸出
-- Test 2 Hello World Test 1 Hello World Test 3 Hello World -- Test 2 Hello World Test 3 Hello World
驅動此系統的程式碼於 [1] 提供。尚待處理:可寫入的參數支援。這在 naim 中是必要的,因為需要修改過程中傳遞的字串;也就是說,一個 filter 模組可能想要將輸入字串中所有「lol」的實例替換為「<grin>」,然後將修改後的字串傳遞到鏈中的其他鉤子。歡迎接受深思熟慮的修補程式。
-- JoshuaWise,2007-02-01
有時候會遇到變數應該在特定函式中有詞彙範圍,但其生命週期又應該大於函式呼叫的衝突情況。在下例中,sounds
表格只由函式 soundit
使用,這表示要把 sounds
放進 soundit
函式,但是每次函式呼叫都要重建 sounds
會很浪費,所以程式設計師通常會把 sounds
放在函式外
local sounds = { a = "ay", b = "bee", c = "see", .... } local function soundit(x) assert(type(x) == "string") return sounds[x] end
在 C 語言中,我們可以讓 sounds
成為 soundit
內部的靜態變數。在 Lua 中,如果想要限制 sounds
的範圍,一般的建議做法是用 do
程式區塊將 sounds
和 soundit
包起來
local soundit; do local sounds = { a = "ay", b = "bee", c = "see", .... } function soundit(x) assert(type(x) == "string") return sounds[x] end end -- note: sounds not visible outside the do-block.
有人抱怨說這樣會讓函式的實作程式碼超出函式範圍,soundit
名稱會重複,而且程式碼的縮排會變深/變得醜陋,看起來不像函式定義。此外,不論 soundit
是否有被呼叫,都會初始化 sounds
(因而造成載入時間的額外負擔)。下述方式會將 sounds
放在函式外,但會在函式內初始化。因為 or
有短路行為,所以一般來說,在呼叫時會帶來較少的額外負擔
local soundit; do local sounds; function soundit(x) sounds = sounds or { a = "ay", b = "bee", c = "see", .... } assert(type(x) == "string") return sounds[x] end end
事實上,我們可以放棄追求完美,讓詞彙範圍延伸,以增強可讀性
local sounds local function soundit(x) sounds = sounds or { a = "ay", b = "bee", c = "see", .... } assert(type(x) == "string") return sounds[x] end
以下有兩個與封閉體建構有關的變體。這些變體比 do
程式區塊的方式更簡潔,但會造成至少建立一個暫時函式的載入時間額外負擔。
local soundit = (function() local sounds = { a = "ay", b = "bee", c = "see", .... } return function(x) assert(type(x) == "string") return sounds[x] end end)()
local soundit = (function() local sounds return function(x) sounds = sounds or { a = "ay", b = "bee", c = "see", .... } assert(type(x) == "string") return sounds[x] end end)()
--DavidManura,2007-03
<樣式說明>(在此處新增更多樣式)