深刻函數 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
語句到另外一個函數