上次看的電腦螢幕程式 rom/programs/monitor.lua
還有一段特別的寫法,是關於 Lua 的 pcall 和 coroutine
這是今天我研究的主題
pcall 有類似於其他語言 try catch 的作用
也就是他可以捕捉錯誤,而不會導致程式意外結束
pcall 的語法如下
status, value = pcall(f, arg1, ...)
status 接收 pcall 第一個回傳值,函數 f 執行成功則回傳 true,否則回傳 false
value 接收 pcall 第二個回傳值,函數 f 執行成功則回傳執行結果,否則回傳錯誤資訊
接下來看看 coroutine
coroutine 乍看之下很像 thread,但似乎不太一樣
它是可以中斷及繼續執行的函式
coroutine 在 create 的時候不會執行
一直到呼叫 resume 才開始執行
monitor.lua 就是將使用者要執行的指令與參數先包裝成 coroutine
然後丟進 pcall 裡頭執行一個無窮迴圈
這段時間就是在等待玩家與螢幕的互動操作
主控權暫時轉移到電腦螢幕上
而下面看到的個事件如下
local monitor = peripheral.wrap(sName)
local previousTerm = term.redirect(monitor)
local co = coroutine.create(function()
(shell.execute or shell.run)(sProgram, table.unpack(tArgs, 3))
end)
local function resume(...)
local ok, param = coroutine.resume(co, ...)
if not ok then
printError(param)
end
return param
end
local timers = {}
local ok, param = pcall(function()
local sFilter = resume()
while coroutine.status(co) ~= "dead" do
local tEvent = table.pack(os.pullEventRaw())
if sFilter == nil or tEvent[1] == sFilter or tEvent[1] == "terminate" then
sFilter = resume(table.unpack(tEvent, 1, tEvent.n))
end
if coroutine.status(co) ~= "dead" and (sFilter == nil or sFilter == "mouse_click") then
if tEvent[1] == "monitor_touch" and tEvent[2] == sName then
timers[os.startTimer(0.1)] = { tEvent[3], tEvent[4] }
sFilter = resume("mouse_click", 1, table.unpack(tEvent, 3, tEvent.n))
end
end
if coroutine.status(co) ~= "dead" and (sFilter == nil or sFilter == "term_resize") then
if tEvent[1] == "monitor_resize" and tEvent[2] == sName then
sFilter = resume("term_resize")
end
end
if coroutine.status(co) ~= "dead" and (sFilter == nil or sFilter == "mouse_up") then
if tEvent[1] == "timer" and timers[tEvent[2]] then
sFilter = resume("mouse_up", 1, table.unpack(timers[tEvent[2]], 1, 2))
timers[tEvent[2]] = nil
end
end
end
end)
term.redirect(previousTerm)
if not ok then
printError(param)
end
這一段其實我覺得已經有點深,我只能看出個大概意思與用途,暫時不再深入程式本身的邏輯
以下繼續介紹 coroutine 其他特性
function hello(n)
print("hello")
coroutine.yield() -- 中斷 coroutine
print("world")
end
local co = coroutine.create(hello)
coroutine.resume(co)
print(", ")
coroutine.resume(co)
yield 除了中斷 coroutine 的執行,也可以執行回傳數值
而 coroutine.resume 回傳的第一個值是 true/false
代表執行成功與否
第二個就是我們自訂的回傳值
function hello(n)
print("hello")
print("world")
coroutine.yield(os.date()) -- 中斷執行並回傳值
print("2021 ironman")
end
local co = coroutine.create(hello)
local ok, date = coroutine.resume(co)
可以透過 coroutine.status 取得 coroutine 的執行狀態
如上述 monitor.lua 持續的判斷 coroutine 是否結束執行
function hello(n)
print("hello")
coroutine.yield()
print("world")
end
local co = coroutine.create(hello)
print(coroutine.status(co)) -- suspended
coroutine.resume(co)
print(coroutine.status(co)) -- suspended
coroutine.close(co)
print(coroutine.status(co)) -- 執行結束的狀態為 dead
今天對 coroutine 的小小研究到這裡
下一回我預計會回到 CC: Tweaked 工具箱裡面繼續挖掘新的方塊!