Lua 設計模式

lua-users home
wiki

此頁面說明了 Lua 的設計模式。請參閱 [Wikipedia:設計模式] 以了解背景。

其他頁面上的設計模式

模式:全域收集器

傳統上,如果我們要定義一個變數或鍵值對的表格,我們會使用表格建構語法 {...}

-- 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 程式區塊將 soundssoundit 包起來

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

樣式:<樣式名稱>

<樣式說明>(在此處新增更多樣式)

另請參閱


RecentChanges · 偏好設定
編輯 · 歷史記錄
上一次編輯時間為 2017 年 8 月 2 日上午 9:58 GMT (diff)