求聞百科:格式手冊/Lua

本页使用了标题或全文手工转换,现处于香港繁体模式
頁面被全保護
求聞百科,共筆求聞

本方針列舉了用Lua語言編寫Scribunto模組應該遵守的一些規定。遵守這些規範有助於Lua代碼更加可讀、效能更佳並易於維護。

代碼邏輯與演算法

不要定義全域變數

在Lua中,定義變數(包括聲明函數)時應該儘可能使用局部變數,避免定義全域變數。例如,下面這個例子就是不恰當的,

myconst = 32
function myutil(s)
  -- 一段代码
end

應當改成:

local myconst = 32
local function myutil(s)
  -- 一段代码
end

對於MediaWiki庫中指定了使用全域變數的,則可以按照MediaWiki庫函數中的操作。此外,暫時規定模組:No globals也是一個例外,該模組會透過修改_G的元表來防止定義全域變數,即透過修改全域變數本身來避免意外修改全域變數。

儘可能使用已有函數

很多模組需要實現的一些功能,沒必要專門透過寫函數來實現,而是儘可能使用已有的函數。例如,MediaWiki提供的mwModule: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:callParserFunctionframe: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庫中的函數或者#運算子。但是,常規的欄位存取以及透過pairsipairs進行迭代仍然是正常的。

代碼格式

空行

空行可以將代碼清晰地分成多個部分。通常,多個函數之間應當空行。

空格

空格可以使得代碼更加美觀。以下情況下,建議空格

  • 運算子之間,例如c = a + b
  • 用逗號分隔的多個變數之間,例如tonumber('32', 7)return a, bfor 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是沒有意義的。