Lua 程式設計/標準庫
Lua 是一種被稱為“沒有提供電池”的語言。這意味著它的庫被保留在執行某些任務所需的最低限度。Lua 依靠其社群建立可用於執行更特定任務的庫。Lua 參考手冊 為所有庫提供文件[1],因此它們將在本文中簡要介紹。除了基本庫和包庫之外,所有庫都將它們的函式和值作為表的欄位提供。
基本庫為 Lua 提供核心功能。它的所有函式和值都直接在全域性環境中可用,並且預設情況下,所有在全域性環境中直接可用的函式和值都是基本庫的一部分。
一個斷言是一個謂詞,開發人員假設它是正確的。它們在程式中使用,以確保特定條件在程式執行的特定時刻為真。斷言用於單元測試中驗證程式是否正常工作,但也用於程式程式碼中,在這種情況下,程式將在斷言為假時失敗,要麼驗證程式執行的環境是否正確,要麼驗證程式程式碼中沒有錯誤,並生成適當的錯誤訊息,以便在某些事情未按預期發生時更容易找到程式碼中的錯誤。在 Lua 中,斷言使用assert函式進行,該函式接受一個條件和一條訊息(預設為“斷言失敗!”)作為引數。當條件計算為假時,assert會丟擲一個帶有訊息的錯誤。當它計算為真時,assert會返回所有引數。
垃圾收集是由 Lua 和許多其他語言實現的一種自動記憶體管理形式。當程式需要在變數中儲存資料時,它會要求作業系統在計算機記憶體中分配空間來儲存變數的值。然後,當它不再需要該空間時(通常是因為變數超出了範圍),它會告訴作業系統釋放該空間,以便其他程式可以使用它。在 Lua 中,實際過程要複雜得多,但基本思想是一樣的:程式必須告訴作業系統它何時不再需要變數的值。在低階語言中,分配由語言處理,但釋放沒有,因為語言無法知道程式設計師何時不再需要值:即使引用該值的變數超出了範圍或被刪除,另一個變數或指令碼中的一個欄位可能仍然引用它,而釋放它會導致問題。在高階語言中,釋放可能由各種自動記憶體管理系統處理,例如垃圾收集,這是 Lua 使用的系統。垃圾收集器定期搜尋 Lua 分配的所有值,以查詢任何地方都沒有引用的值。它將收集程式不再可以訪問的值(因為沒有對它們的引用),並且因為它知道這些值可以安全地釋放,所以會釋放它們。所有這些都是透明且自動完成的,因此程式設計師通常不需要為此做任何事情。但是,有時,開發人員可能希望對垃圾收集器發出指示。
弱引用是指垃圾收集器會忽略的引用。這些引用由開發人員使用mode元方法指示給垃圾收集器。表的mode元方法應該是一個字串。如果該字串包含字母“k”,則該表所有欄位的鍵都是弱的,如果包含字母“v”,則該表所有欄位的值都是弱的。當一個物件陣列具有弱值時,即使這些物件在該陣列中被引用,垃圾收集器也會收集它們,只要它們只在該陣列和其他弱引用中被引用即可。這種行為有時很有用,但很少使用。
可以使用collectgarbage函式操縱垃圾收集器,該函式是基本庫的一部分,充當垃圾收集器的介面。它的第一個引數是一個字串,它指示垃圾收集器應該執行什麼操作;第二個引數由某些操作使用。collectgarbage函式可用於停止垃圾收集器、手動執行收集週期以及計算 Lua 使用的記憶體。
—維基百科, 協程
協程是可以在 Lua 中使用協程庫建立和操縱的元件,這些元件允許透過從自身內部呼叫會產生協程的函式或從外部呼叫恢復協程的函式,在特定位置產生和恢復函式的執行。示例
- 主執行緒中的一個函式使用
coroutine.create從一個函式建立協程,並使用coroutine.resume恢復它,並將數字 3 傳遞給它。 - 協程中的函式執行並獲得作為
coroutine.resume引數傳遞的數字。 - 該函式在執行過程中的某個點呼叫
coroutine.yield,並將它收到的引數(3)和 2 的總和(因此 3+2=5)作為引數傳遞。 - 對
coroutine.resume的呼叫返回 5,因為它被傳遞給了coroutine.yield,並且現在再次執行的主執行緒將該數字儲存在一個變數中。它在執行了一些程式碼後再次恢復協程,並將它從coroutine.resume呼叫中收到的值的雙倍傳遞給coroutine.resume(即它傳遞 5×2=10)。 - 協程獲得作為
coroutine.yield呼叫結果傳遞給coroutine.resume的值,並在執行更多程式碼後終止。它返回coroutine.yield呼叫結果和最初作為引數給它的值之間的差(即 10−3=7)。 - 主執行緒獲取作為
coroutine.resume呼叫結果由協程返回的值,並繼續執行。
此示例在程式碼中給出以下結果
local co = coroutine.create(function(initial_value)
local value_obtained = coroutine.yield(initial_value + 2) -- 3+2=5
return value_obtained - initial_value -- 10-3=7
end)
local _, initial_result = coroutine.resume(co, 3) -- initial_result: 5
local _, final_result = coroutine.resume(co, initial_result * 2) -- 5*2=10
print(final_result) --> 7
coroutine.create 函式使用函式建立一個協程;協程是“執行緒”型別的值。coroutine.resume 啟動或繼續協程的執行。當協程遇到錯誤或沒有剩餘可執行內容時,協程就被認為是死亡的(在這種情況下它會終止執行)。當協程死亡時,它無法恢復。如果協程的執行成功,coroutine.resume 函式將返回 true,以及所有返回的值(如果協程已終止)或傳遞給 coroutine.yield 的值(如果它尚未終止)。如果執行不成功,它將返回 false 以及錯誤訊息。coroutine.resume 返回正在執行的協程和 true(當該協程為主執行緒時),否則返回 false。
coroutine.status 函式以字串形式返回協程的狀態。
- 如果協程正在執行,則為“running”,這意味著它必須是呼叫
coroutine.status的協程。 - 如果協程在呼叫 yield 時被掛起,或者它尚未開始執行,則為“suspended”。
- 如果協程處於活動狀態但未執行,則為“normal”,這意味著它已恢復另一個協程。
- 如果協程已完成執行或遇到錯誤,則為“dead”。
coroutine.wrap 函式返回一個函式,每次呼叫該函式都會恢復協程。傳遞給此函式的額外引數將充當傳遞給 coroutine.resume 的額外引數,並且由協程返回或傳遞給 coroutine.yield 的值將由對該函式的呼叫返回。與 coroutine.resume 不同,coroutine.wrap 函式不會在受保護模式下呼叫協程,並且會傳播錯誤。
協程有很多用例,但描述它們超出了本書的範圍。
在操作字串時,能夠在字串中搜索符合特定模式的子字串通常很有用。Lua 擁有一個字串操作庫,它提供用於執行此操作的函式,以及用於表達這些函式可以在字串中搜索的模式的符號。Lua 提供的符號與正則表示式非常相似,這是一種用於表達程式設計世界中大多數語言和工具使用的模式的符號。但是,它更有限,並且具有略微不同的語法。
字串庫的 find 函式在字串中查詢模式的第一個匹配項。如果它在字串中找到了模式的出現,它將返回字串中的索引(表示字串中字元位置的整數,從第一個字元開始,位於位置 1),其中出現開始和結束。如果它沒有找到模式的出現,它將不返回任何內容。它接受的第一個引數是字串,第二個引數是模式,第三個引數是表示 find 函式應該開始搜尋的字元位置的整數。最後,find 函式可以被告知透過傳遞 true 作為其第四個引數來執行簡單的匹配,而無需使用模式。然後,它將只在第一個字串中搜索給定的第二個字串的出現。當執行簡單匹配時,必須給出第三個引數。此示例程式碼在句子中搜索單詞“lazy”,並列印找到的單詞的開始和結束位置。
local start_position, end_position = string.find("The quick brown fox jumps over the lazy dog.", "lazy", 1, true)
print("The word \"lazy\" was found starting at position " .. start_position .. " and ending at position " .. end_position .. ".")
此程式碼給出的結果為The word "lazy" was found starting at position 36 and ending at position 39.。它等效於以下內容
local sentence = "The quick brown fox jumps over the lazy dog."
local start_position, end_position = sentence:find("lazy", 1, true)
print("The word \"lazy\" was found starting at position " .. start_position .. " and ending at position " .. end_position .. ".")
這是可行的,因為字串的 index 元方法被設定為包含字串庫函式的表,使得能夠用 b:a(...) 替換 string.a(b, ...)。
字串庫中接受索引以指示字元位置或返回此類索引的函式將第一個字元視為位於位置 1。它們接受負數並將它們解釋為從字串末尾向後索引,最後一個字元位於位置 -1。
模式是遵循特定符號的字串,以指示字串可能匹配或不匹配的模式。為此,模式包含字元類,它們代表字元集的組合。
| 字元組合 | 描述 |
|---|---|
| . | 所有字元 |
| %a | 字母(大寫和小寫) |
| %c | 控制字元 |
| %d | 數字 |
| %g | 可列印字元(空格字元除外) |
| %l | 小寫字母 |
| %p | 標點符號字元 |
| %s | 空格字元 |
| %u | 大寫字母 |
| %w | 字母數字字元(數字和字母) |
| %x | 十六進位制數字 |
所有不是特殊字元的字元都代表它們自己,特殊字元(所有不是字母數字的字元)可以透過在它們前面加上百分號來轉義。字元類可以透過放在集合中來組合以建立更大的字元類。集合被標記為在方括號之間標記的字元類(即 [%xp] 是所有十六進位制字元加上字母“p”的集合)。字元範圍可以透過用連字元(例如 [0-9%s] 代表從 0 到 9 的所有數字加上空格字元)分隔範圍的結束字元(按升序排列)來表示。如果脫字元 (“^”) 字元放在集合的開頭(緊接在左方括號之後),則集合將包含所有字元,除了它在該脫字元未放在集合的開頭時會包含的字元。
所有由帶百分號的字母表示的類的補碼可以表示為一個百分號後跟相應的字母(例如 %S 表示除空格字元以外的所有字元)。
模式是模式項的序列,這些序列代表了字串應該匹配哪些序列才能與它匹配。模式項可以是字元類,在這種情況下,它只匹配類中的任何一個字元一次;字元類後跟 “*” 字元,在這種情況下,它匹配類中 0 個或多個字元的重複(這些重複項將始終匹配最長的可能序列);字元類後跟加號 (“+”) 字元,在這種情況下,它匹配類中 1 個或多個字元的重複(這些重複項也將始終匹配最長的可能序列);字元類後跟減號 (“-”) 字元,在這種情況下,它匹配類中 0 個或多個字元的重複,但匹配最短的可能序列;或者字元類後跟問號,在這種情況下,它匹配類中一個或沒有一個字元的出現。
可以匹配等效於先前捕獲的子字串的子字串:%1 將匹配第一個捕獲的子字串,%2 匹配第二個子字串,依此類推,直到 %9。捕獲在下面描述。模式提供了另外兩種功能,如參考手冊所述
%bxy,其中 x 和 y 是兩個不同的字元;這樣的專案匹配以 x 開頭、以 y 結尾的字串,並且 x 和 y 是平衡的。這意味著,如果從左到右讀取字串,對 x 計數 +1,對 y 計數 -1,則結束的 y 是計數達到 0 的第一個 y。例如,專案%b()匹配具有平衡括號的表示式。
%f[set],一個邊界模式;這樣的專案在任何位置匹配一個空字串,使得下一個字元屬於集合,而前一個字元不屬於集合。集合 set 的解釋如前所述。主體開頭和結尾的處理方式就像它們是字元 '\0' 一樣。—Lua 作者, Lua 5.2 參考手冊
模式是模式項的序列,前面可以選擇加一個脫字元,表示模式只能匹配字串的開頭,後面可以選擇加一個美元符號,表示模式只能匹配字串的結尾。據說這些符號錨定字串開頭或結尾的匹配。這兩個字元只有在模式開頭或結尾時才具有特殊含義。
子模式可以包含在模式內的括號內,以指示捕獲。當匹配成功時,與捕獲匹配的字串的子字串將被儲存以供將來使用,例如,被 gmatch 返回。它們總是從其左括號的位置開始編號。兩個空括號表示空捕獲,它捕獲當前字串位置(它是一個數字,不是字串的一部分)。
gmatch 函式可以用於迭代字串中模式的出現;與 find 函式不同,不可能指定開始搜尋的初始位置或執行簡單匹配。gmatch 函式返回一個迭代器,當呼叫時,它會返回字串中給定模式的下一個捕獲。如果沒有在模式中指定捕獲,則會給出整個匹配。以下示例顯示瞭如何迭代句子中的單詞,並逐個列印它們
local sentence = "The quick brown fox jumps over the lazy dog."
for word in sentence:gmatch('%a+') do
print(word)
end
在此示例中,整個匹配由迭代器返回的唯一值 word 給出。
gsub 函式可用於將字串中模式的所有出現替換為其他內容。它的前兩個引數是字串和模式,而第三個引數是要替換出現的字串,第四個引數是要替換出現的最大次數。第三個引數可以是表或函式,而不是字串。
當第三個引數是字串時,它被稱為替換字串,它將字串中匹配模式的出現次數替換掉。由模式儲存的捕獲可以嵌入到替換字串中;它們用百分號後跟代表捕獲編號的數字來表示。匹配本身可以用%0表示。替換字串中的百分號必須轉義為%%。
當第三個引數是表格時,第一個捕獲被用作索引該表格的鍵,而替換字串是表格中對應於該鍵的值。當它是一個函式時,該函式會在每次匹配時被呼叫,所有捕獲都作為引數傳遞。在這兩種情況下,如果沒有捕獲,則使用整個匹配。如果函式或表格返回值false或nil,則不會進行任何替換。
以下是一些直接來自 Lua 5.2 參考手冊的示例
x = string.gsub("hello world", "(%w+)", "%1 %1") --> x="hello hello world world" x = string.gsub("hello world", "%w+", "%0 %0", 1) --> x="hello hello world" x = string.gsub("hello world from Lua", "(%w+)%s*(%w+)", "%2 %1") --> x="world hello Lua from" x = string.gsub("home = $HOME, user = $USER", "%$(%w+)", os.getenv) --> x="home = /home/roberto, user = roberto" x = string.gsub("4+5 = $return 4+5$", "%$(.-)%$", function (s) return load(s)() end) --> x="4+5 = 9" local t = {name="lua", version="5.2"} x = string.gsub("$name-$version.tar.gz", "%$(%w+)", t) --> x="lua-5.2.tar.gz"—Lua 作者, Lua 5.2 參考手冊
Lua 提供了除模式匹配之外的其他用於操作字串的函式。這些函式包括reverse函式,它返回一個字元順序反轉的字串,lower函式,它返回一個字串的小寫等效項,upper函式,它返回一個字串的大寫等效項,len函式,它返回一個字串的長度,以及sub函式,它返回一個字串的子字串,該子字串從給定的兩個字元位置作為引數開始並結束。還有更多,它們在參考手冊中都有記錄。
- ↑ Ierusalimschy, Roberto; Celes, Waldemar; Henrique de Figueiredo, Luiz. Lua 5.2 參考手冊. 檢索於 2013 年 11 月 30 日.