深入函式 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陳述句到另一個函式
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/3888.html
標籤:其他
