本條例列舉了用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
在每個頁面均只會運行和加載一次目標模塊頁面的數據,而不是每個模塊都加載一次。這對含有大量數據的模塊而言,性能非常高。
注意,使用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
是沒有意義的。