lua學習之深入函式第二篇
阿新 • • 發佈:2020-02-26
深入函式 2
非全域性的函式
- 函式是第一類值,函式可以儲存到全域性變數,區域性變數,table 欄位中
- lua 函式庫中的大部分函式儲存到 table 欄位中
Lib = {} Lib.foo = function (x, y) return x + y end Lib.goo = function (x, y) return x - y end Lib = { foo = function (x, y) return x + y end, goo = function (x, y) return x - y end } Lib = {} function Lib.foo(x, y) return x + y end fucntion Lib.goo(x, y) return x - y end
- 將一個函式儲存到一個區域性變數中,即為「區域性函式」
- 該函式只能在對應的作用域使用,對於「程式包」
package
很有用 - lua 將每一個程式塊當作一個函式來處理
- 在程式塊中宣告的函式就是區域性函式,只在該程式塊中可見
- 詞法域確保了程式包中的其他函式可以使用這些區域性函式。
local f = function (<引數列表>) <函式體> end local g = function (<引數列表>) <函式程式碼> f(實參) -- 可以呼叫 f <函式程式碼> end local function f(<引數列表>) <函式體> end -- 階乘 n! = n * (n - 1) * (n - 2) * ... 1 local fact = function (n) -- 錯誤的遞迴函式定義 if n == 0 then return 1 else return n * fact(n - 1) -- fact 函式定義未完成,呼叫的是 fact 全域性變數,而不是 fact 函式本身 end end -- 正確的遞迴函式定義 local fact fact = function (n) if n == 0 then return 1 else return n * fact(n - 1) end end local function foo(<引數>) <函式體> end -- Lua 將其展開為: local foo foo = function (<引數>) <函式體> end -- 正確的函式定義,對於間接遞迴無效 local function fact (n) if n == 0 then return 1 else return n * fact(n - 1) end end -- 遞迴就是函式呼叫函式本身 -- 間接遞迴就是 a 函式呼叫 b 函式而 b 函式又呼叫了 a 函式 -- 間接遞迴需要使用明確的前向宣告 local f, g function g () <函式程式碼> f() <函式程式碼> end function f() -- 不要加 local 如果加上那麼在函式 g 中引用的就處於未定義狀態,因為 lua 會建立一個全新的區域性變數 f <函式程式碼> g() <函式程式碼> end
正確的尾呼叫
- 「尾呼叫」是類似於
goto
的函式呼叫 - 當一個函式呼叫是另一個函式的最後一個動作時,該呼叫就是一條「尾呼叫」
function f (x)
<函式程式碼>
return g(x)
end
- f 呼叫完 g 之後就沒有執行其他程式碼了
- 在這種情況下,程式就不需要返回「尾呼叫」所在的函數了
- 在「尾呼叫」之後,程式無需儲存任何關於該函式的棧資訊
- 當 g 返回時,執行控制權可以直接返回呼叫 f 的那個點上
- 使得在進行「尾呼叫」時不耗費任何棧空間
- 這種實現稱為「尾呼叫消除」
-- 尾呼叫函式 function foo(n) if n > 0 then return foo(n - 1) end end -- 呼叫完 g 函式後還進行了加法操作,非尾呼叫 return g(x) + 1 -- 有 or 操作,必須調整為一個返回值 retrun x or g(x) -- 函式外巢狀一個括號,強制其只返回一個返回值 return (g(x)) -- 尾呼叫標準格式 return <func>(<args>) -- 是一個尾呼叫 -- 呼叫前會對 <func> 及其引數求值 return x[i].foo(x[j] + a * b, i + j)
編寫狀態機
- 典型例子:迷宮
-- 四間房間的迷宮
function room1()
local move = io.read()
if move == "south" then
return room3()
elseif move == "east" then
return room2()
else
print("invalid move")
return room1()
end
end
function room2()
local move = io.read()
if move == "south" then
return room4()
elseif move == "west" then
return room1()
else
print("invalid move")
return room2()
end
end
function room3()
local move = io.read()
if move == "north" then
return room1()
elseif move == "east" then
return room4()
else
print("invalid move")
return room3()
end
end
function room4()
print("congratulations!")
end
- 若沒有「尾呼叫消除」,每次使用者移動都會建立一個新的棧層,若干步後可能會棧溢位
- 「尾呼叫消除」多使用者移動的次數沒有任何限制
- 因為每次移動實際上只是完成一條
goto
語句到另一個函式