快速掌握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”。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值