快速掌握Lua 5.3 —— packages


Q:Lua如何管理”package”?

A:Lua使用”table”來表示”package”,就像Lua標准庫的做法一樣。我們也可以使用”table”來創建自己的”package”。

-- "complex.lua"文件中,一個實現對復數運算的"package"。
complex = {}

-- 用一個"table"表示一個復數,"r"是實部,"i"是虛部。
function complex.new (r, i) return {r=r, i=i} end

-- defines a constant 'i'
complex.i = complex.new(0, 1)

function complex.add (c1, c2)
  return complex.new(c1.r + c2.r, c1.i + c2.i)
end

function complex.sub (c1, c2)
  return complex.new(c1.r - c2.r, c1.i - c2.i)
end

function complex.mul (c1, c2)
  return complex.new(c1.r*c2.r - c1.i*c2.i,
                     c1.r*c2.i + c1.i*c2.r)
end

function complex.div (c1, c2)
  local n = c2.r^2 + c2.i^2
  return complex.new(
             (c1.r * c2.r + c1.i * c2.i) / n, 
             (c1.i * c2.r - c1.r * c2.i) / n
         )
end

--[[ 這個"return"不是必需的,一般加載"package"使用"require",
     同時"package"的名字已經被放在全局變量中了。
     但在"package"的最后返回自己是個好習慣,
     它允許你用除了"require"以外的方式使用"package",比如"dofile()"。]]
return complex

-- "test.lua"文件中。
-- "require"方式。
require "complex"

c = complex.add(complex.i, complex.new(10, 20))
for i,v in pairs(c)
do
    io.write(string.format("%s = %d, ", i, v))    --> r = 10, i = 21, 
end

-- "dofile()"方式。
p = dofile("complex.lua")

c = p.add(p.i, p.new(10, 20))
for i,v in pairs(c)
do
    io.write(string.format("%s = %d, ", i, v))
end

io.write("\n")

Q:如何將”package”中的方法設置為私有的?

A:將方法設置為local。我們定義一個檢查參數的私有方法,

-- "complex.lua"文件中。
complex = {}

-- 私有方法"checkComplex()",外部無法調用。
local function checkComplex (c)
  if not ((type(c) == "table") and
    tonumber(c.r) and tonumber(c.i)) then
    error("bad complex number", 3)
  end
end

function complex.add (c1, c2)
  checkComplex(c1);
  checkComplex(c2);
  return P.new(c1.r + c2.r, c1.i + c2.i)
end

-- 其他相同的方法不列舉。
...

可以注意到,私有方法定義與調用都不需要complex前綴,而公有方法需要。書寫complex前綴實在是太麻煩了,同時我們將公有方法轉私有,或者私有方法轉公有時都需要注意是否需要書寫這個前綴。
這里有個方法解決這些問題,可以將”package”內部的所有函數定義為local,然后在最后將需要導出的函數放入全局表中導出,

-- "complex.lua"文件中。
complex = {}

-- 以下的所有方法在定義時都去掉了前綴,並且均聲明為私有方法("local")。

local function new (r, i) return {r=r, i=i} end

local function checkComplex (c)
    if not ((type(c) == "table") and
        tonumber(c.r) and tonumber(c.i)) then
        error("bad complex number", 3)
    end
end

i = new(0, 1)

local function add (c1, c2)
    checkComplex(c1);
    checkComplex(c2);
    return new(c1.r + c2.r, c1.i + c2.i)
end

local function sub (c1, c2)
    return new(c1.r - c2.r, c1.i - c2.i)
end

local function mul (c1, c2)
    return new(c1.r*c2.r - c1.i*c2.i,
    c1.r*c2.i + c1.i*c2.r)
end

function div (c1, c2)
    local n = c2.r^2 + c2.i^2
    return new(
    (c1.r * c2.r + c1.i * c2.i) / n,
    (c1.i * c2.r - c1.r * c2.i) / n
    )
end

-- 公有的方法才放在"complex"全局表中導出。
complex = {
    new = new,
    add = add,
    sub = sub,
    mul = mul,
    div = div,
    i = i,
}

return complex

現在,”package”中的方法終於不再需要書寫前綴complex了,在”package”內部無論調用公有方法還是私有方法都是相同的方式。而且修改方法的公有性或私有性也很簡單,直接修改全局complex表中的內容就好了。

附加:

1、”如何將’package’中的方法設置為私有的?”的”Q & A”中優化了”package”中方法的定義方式,使得無論是公有還是私有方法都擺脫了”package”名字前綴。不過與此同時,每個方法都需要顯式定義為local類型的,這很容易造成錯誤。一旦不小心漏寫,就又將方法定義為全局的了。
解決這個問題,可以將”package”定義在獨立的環境中,

-- "complex.lua"文件中。
complex = {}
-- 原先環境中的函數或變量需要能被訪問,比如下面的"type()"。
setmetatable(complex, {__index = _ENV})
-- 下面的所有函數都會定義在獨立的"complex"環境中。
_ENV = complex

-- 因為在獨立的環境中,函數均定義成全局的也無所謂了。

function new (r, i) return {r=r, i=i} end

function checkComplex (c)
    if not ((type(c) == "table") and
        tonumber(c.r) and tonumber(c.i)) then
        error("bad complex number", 3)
    end
end

i = new(0, 1)

function add (c1, c2)
    checkComplex(c1);
    checkComplex(c2);
    return new(c1.r + c2.r, c1.i + c2.i)
end

function sub (c1, c2)
    return new(c1.r - c2.r, c1.i - c2.i)
end

function mul (c1, c2)
    return new(c1.r*c2.r - c1.i*c2.i,
    c1.r*c2.i + c1.i*c2.r)
end

function div (c1, c2)
    n = c2.r^2 + c2.i^2
    return new(
    (c1.r * c2.r + c1.i * c2.i) / n,
    (c1.i * c2.r - c1.r * c2.i) / n
    )
end

return complex

這種方式有一個有趣的副作用,例如你為了安全性,屏蔽了_ENV中的io庫(io.open()io.read()io.write()等),但別人可以通過你提供的”package”訪問你屏蔽的函數(例如complex.io.write()等)。
2、因為”package”本身就是”table”,所以我們可以在”package”中內嵌”package”。


注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
  © 2014-2022 ITdaan.com 联系我们: