1. 程式人生 > >lua學習之深入函式第二篇

lua學習之深入函式第二篇

深入函式 2

非全域性的函式

  1. 函式是第一類值,函式可以儲存到全域性變數,區域性變數,table 欄位中
  2. 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
  1. 將一個函式儲存到一個區域性變數中,即為「區域性函式」
  2. 該函式只能在對應的作用域使用,對於「程式包」package 很有用
  3. lua 將每一個程式塊當作一個函式來處理
  4. 在程式塊中宣告的函式就是區域性函式,只在該程式塊中可見
  5. 詞法域確保了程式包中的其他函式可以使用這些區域性函式。
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

正確的尾呼叫

  1. 「尾呼叫」是類似於 goto 的函式呼叫
  2. 當一個函式呼叫是另一個函式的最後一個動作時,該呼叫就是一條「尾呼叫」
function f (x)
    <函式程式碼>
    return g(x)
end
  1. f 呼叫完 g 之後就沒有執行其他程式碼了
  2. 在這種情況下,程式就不需要返回「尾呼叫」所在的函數了
  3. 在「尾呼叫」之後,程式無需儲存任何關於該函式的棧資訊
  4. 當 g 返回時,執行控制權可以直接返回呼叫 f 的那個點上
  5. 使得在進行「尾呼叫」時不耗費任何棧空間
  6. 這種實現稱為「尾呼叫消除」
-- 尾呼叫函式
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)

編寫狀態機

  1. 典型例子:迷宮
-- 四間房間的迷宮
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
  1. 若沒有「尾呼叫消除」,每次使用者移動都會建立一個新的棧層,若干步後可能會棧溢位
  2. 「尾呼叫消除」多使用者移動的次數沒有任何限制
  3. 因為每次移動實際上只是完成一條 goto 語句到另一個函式