本方針列舉了用Lua語言編寫Scribunto模組應該遵守的一些規定。遵守這些規範有助於Lua代碼更加可讀、效能更佳並易於維護。
代碼邏輯與演算法
不要定義全域變數
在Lua中,定義變數(包括聲明函式)時應該儘可能使用局部變數,避免定義全域變數。例如,下面這個例子就是不恰當的,
myconst = 32
function myutil(s)
-- 一段代码
end
應當改成:
local myconst = 32
local function myutil(s)
-- 一段代码
end
對於MediaWiki庫中指定了使用全域變數的,則可以按照MediaWiki庫函式中的操作。此外,暫時規定模組:No globals也是一個例外,該模組會透過修改_G
的元表來防止定義全域變數,即透過修改全域變數本身來避免意外修改全域變數。
儘可能使用已有函式
很多模組需要實現的一些功能,沒必要專門透過寫函式來實現,而是儘可能使用已有的函式。例如,MediaWiki提供的mw
、Module:TableTools等元模組、libraryUtil
庫都提供了相當實用的函式。這些情況下應該儘可能使用已經提供好的函式,而不需要自己去定義一個新的。例如:
local function trim(s)
return s:match "^%s+(.-)%s+$"
end
就完全可以寫成:
local trim = mw.text.trim
對於很多模組都單獨聲明了,且沒有已有函式提供該功能的,可以將其合併到新的或已有的元模組的函式中。
避免在Lua中展開模板或解析wiki文字
在Lua模板中,能用模組解決的,就不要呼叫模板。例如,在代碼中,如需呼叫模板連結:{{navbox}},應該呼叫require 'Module:Navbox'._navbox
,而不是frame:expandTemplate{name = 'Navbox', args = 參數}
這樣會快得多。
能直接使用frame:callParserFunction
或frame:extensionTag
的(以及某些情況確實需要呼叫frame:expandTemplate
的),就不要使用frame:preprocess
。
能使用mw
庫中的函式解決的,就避免去呼叫解析器函式。例如,已經有tostring(mw.title.getCurrentTitle())
,就不要去使用frame:callParserFunction('FULLPAGENAME')
,更不要使用frame:preprocess '{{FULLPAGENAME}}'
。
嚴禁頻繁連接字串
有時候需要對輸出的字串逐個增加,例如:
local result = ''
for _, s in ipairs(my_list) do
result = result .. '* ' .. s
end
return result
這種行為稱為頻繁連接字串。這樣每連接一次,都會建立新的字串對象,然後將舊的字串作為垃圾回收,影響效能。
如果需要多次追加字串,應當使用表(陣列),最後使用table.concat
連接。此外,頻繁往表(陣列)的末尾處追加時,建議使用t[#t + 1] = v
而非table.insert(t, v)
,這是因為Lua最原始的運算操作往往比呼叫函式更快。
local result = {}
for _, s in ipairs(my_list) do
result[#result + 1] = '* ' .. s
end
return table.concat(result)
在本例中,還可以使用更加友好、更加靈活的mw.html
庫,例如:
local result = mw.html.create 'ul'
for _, s in ipairs(my_list) do
result:tag 'li':wikitext(s)
end
return tostring(result)
區分普通模組與資料
有時候需要在模組記憶體儲表形式的大量資料。這種情況下,應該將資料放在專門的模組頁面中,然後在執行時使用mw.loadData
而非require
取得這些資料。與require
相比,mw.loadData
的優點在於,無論透過多少模板、模組執行多少次,mw.loadData
在每個頁面均只會執行和載入一次目標模組頁面的資料,而不是每個模組都載入一次。這對含有大量資料的模組而言,效能非常高。
目前求聞百科有許多資料模組,例如Module:CGroup的子頁面(公共轉換組)、Module:RailSystems的子頁面(軌道交通系統)等。這些資料模組都應該使用mw.loadData
載入。
注意,使用mw.loadData
載入的表是唯讀的,並且是透過元表來存取欄位的。因此mw.loadData
返回的對象不能寫入任何欄位(否則會出錯),也不能使用table庫中的函式或者#
運算子。但是,常規的欄位存取以及透過pairs
、ipairs
進行迭代仍然是正常的。
代碼格式
空行
空行可以將代碼清晰地分成多個部分。通常,多個函式之間應當空行。
空格
空格可以使得代碼更加美觀。以下情況下,建議空格:
- 運算子之間,例如
c = a + b
。 - 用逗號分隔的多個變數之間,例如
tonumber('32', 7)
、return a, b
、for k, v in pairs(t) do
。- 注意:函式名稱和參數之間不空格,但緊接字串字面量、省略括號的情況除外。如
f(32)
不建議寫成f (32)
、print 'Hello'
不建議寫成print'Hello'
。
- 注意:函式名稱和參數之間不空格,但緊接字串字面量、省略括號的情況除外。如
- 單行注釋的兩個橫線之後建議加一個空格。如代碼後同一行內加入注釋,兩個橫線之前建議加至少兩個空格。
縮排
縮排可以使得代碼層次清晰。雖然不像Python那樣必要,但是沒有縮排的代碼以及縮排混亂的代碼必然十分難讀。
每一級縮排一般使用「制表符」(在代碼編輯器中按鍵盤上的Tab鍵,手機版可能無法直接輸入制表符,可以複製代碼中已有的制表符)。儘管很多IDE都推薦使用空格代替制表符,但是在求聞百科不強求這麼做。
此外,對於MediaWiki庫的mw.html對象,可以在鏈式呼叫中根據層次邏輯調整縮排。
一個Lua頁面內不允許混用不同類型(制表符、空格)縮排。要麼全用空格,要麼全用制表符。
注釋
注釋用於在代碼中記錄著作權協定、函式用法或提醒編輯者等內容,使得讀者更加能夠理解注釋內容。注釋可以用於:
- 闡明函式的返回值類型和用途,以及參數的類型和用途
- 將不再需要的代碼注釋掉。
- 說明代碼中的邏輯。
此外,注釋不應當「說廢話」。例如x = x + 1 -- 將x增加1
是沒有意義的。