iT邦幫忙

2021 iThome 鐵人賽

DAY 11
0
Software Development

在麥塊的農場裡寫 Lua系列 第 11

Day11 為什麼電腦能自動完成指令 - Lua 的多值回傳

從前幾回的研究中,我已經學會 Lua 的函數宣告,也只是一個 function 變數,以及相關的語法糖
今天我想來探索 Lua 的函數多值回傳特性
這在之前學習迭代函數與迴圈時,就有看到這樣的語法

for _, sFile in ipairs(tApis) do
    -- 略 --
end

而主題則是,CC: Tweaked 電腦如何在我輸入的過程中,猜到或預期可能的指令?
如以下畫面
CC: Tweaked Computer Auto-Complete

現在我已經知道,shell.lua 有個無窮迴圈,會持續等待玩家的輸入
也就是以下這段程式碼,是我上次暫時沒深入研究的部分
term 的操作,是 /rom/apis/term.lua
看起來這邊只是在設定畫面或文字,所以我也先略過
tCommandHistory 看來是紀錄每次玩家的指令
以便可以一直按上下鍵來快速執行之前的指令

-- Read commands and execute them
local tCommandHistory = {}
while not bExit do
    term.redirect(parentTerm)
    term.setBackgroundColor(bgColour)
    term.setTextColour(promptColour)
    write(shell.dir() .. "> ")
    term.setTextColour(textColour)


    local sLine
    if settings.get("shell.autocomplete") then
        sLine = read(nil, tCommandHistory, shell.complete)
    else
        sLine = read(nil, tCommandHistory)
    end
    if sLine:match("%S") and tCommandHistory[#tCommandHistory] ~= sLine then
        table.insert(tCommandHistory, sLine)
    end
    shell.run(sLine)
end

所以關鍵應該是這一行

sLine = read(nil, tCommandHistory, shell.complete)

為了印證我的假設,我直接在「電腦」的硬碟上,新增一個 test2.lua 以及上面這一行,執行結果如下
CC: Tweaked Computer Auto-Complete

接下來就有兩個 function 可以持續深入
分別是 readshell.complete
shell.complete 有相對完整的註解說明
很明顯這就是持續接收我的輸入,並猜測我要打的指令了!

-- This accepts an incomplete command, and completes the program name or
-- arguments. For instance, `l` will be completed to `ls`, and `ls ro` will be
-- completed to `ls rom/`.

Lua 函數的多值回傳

read 則是在 bios.lua #181
但我只想先專注在多值回傳的部分

function read(_sReplaceChar, _tHistory, _fnComplete, _sDefault)
    -- 略 --

    local w = term.getSize()
    local sx = term.getCursorPos()

    -- 重新設定游標位置與輸出字串,若有設定 complete function 則再設定顏色與猜測的指令 --
    local function redraw(_bClear)
        -- 略 --
        local _, cy = term.getCursorPos()
        term.setCursorPos(sx, cy)
        -- 略 --
        if nCompletion then
            -- 略 --
        end

        term.setCursorPos(sx + nPos - nScroll, cy)
    end

    -- 略 --
end

再參考 /rom/apis/window.lua
顯然這兩個 function 預設是回傳多個值

function window.getSize()
    return nWidth, nHeight
end

function window.getCursorPos()
    return nCursorX, nCursorY
end

但上述的指令只用一個值來接收
這樣子就會只有取得第一個回傳值,第二個值就自動忽略了
相當於以下的語法,底線就是忽略的意思,這和 Golang 語法特性是相同的

local w, _ = term.getSize()
local sx, _ = term.getCursorPos()

反之,另一行敘述就是只取第二個值,忽略第一個值

local _, cy = term.getCursorPos()

以下是更多的語法特性實驗

function getCursorPos() return 1, 2 end
local x, y, z = getCursorPos()
print(x, y, z)  -- 1  2  nil
print(getCursorPos())     -- 1  2

local x, y, z = 6, getCursorPos()
print(x, y, z)  -- 6  1  2
print(6, getCursorPos())  -- 6  1  2

local x, y = getCursorPos(), 5
print(x, y)     -- 1  5
print(getCursorPos(), 5)  -- 1  5
print(getCursorPos() .. '23')  -- 123

function getCursorPos() end
local x, y = getCursorPos(), 5, 7
print(x, y)     -- nil  5

根據實驗結果可以知道
函數回傳值不夠多的時候,多餘的接收參數都會是 nil
而且函數的多值回傳必須是最後的陳述式,否則只會回傳第一個值

以上是今天的分享與研究心得~


上一篇
Day10 為什麼電腦懂我的指令?函數宣告 part2
下一篇
Day12 安裝外接磁碟機與磁碟片 - 取得 Lua shell script 的參數列
系列文
在麥塊的農場裡寫 Lua30

尚未有邦友留言

立即登入留言