15.3 Using Environments
使用环境
A major drawback of that basic method for creating modules is that it calls for special attention from the programmer. She must qualify names when accessing other public entities inside the same module. She has to change the calls whenever she changes the status of a function from private to public (or from public to private). Moreover, it is all too easy to forget a local in a private declaration.
那种创建模块的基本方法的一个主要缺点是,它要求程序员格外注意。她必须在访问同模块内的其他实体时限定名字。在从私有到公有(或从公有到私有)改变函数的状态时,必须修改调用。此外,在私有声明中忘记local实在太容易了。
Function environments offer an interesting technique for creating modules that solves all these problems. Once the module main chunk has an exclusive environment, not only all its functions share this table, but also all its global variables go to this table. Therefore, we can declare all public functions as global variables and they will go to a separate table automatically. All the module has to do is to assign this table to the module name and also to package.loaded. The next code fragment illustrates this technique:
函数环境为创建解决所有这些问题的模块提供了一项有趣的技术。一旦模块主程序块具有独占的环境,不只其所有函数共享该表,而且其所有全局变量都进入该表。因此,我们可声明所有公用函数为全局变量,它们会自动进入一个单独的表。模块所要做的只是把该表赋给模块名和package.loaded。下一个代码片段例示该技术:
local modname = ...
local M = {}
_G[modname] = M
package.loaded[modname] = M
setfenv(1, M)
Now, when we declare function add, it goes to complex.add:
现在,当我们声明函数add,它定位于complex.add:
function add (c1, c2)
return new(c1.r + c2.r, c1.i + c2.i)
end
Moreover, we can call other functions from the same module without any prefix. For instance, add gets new from its environment, that is, it gets complex.new.
而且,我们可不带任何前缀从同模块中调用其他函数。例如,add从其环境中得到new,即,它得到了complex.new。
This method offers a good support for modules, with little extra work for the programmer. It needs no prefixes at all. There is no difference between calling an exported and a private function. If the programmer forgets a local, he does not pollute the global namespace; instead, a private function simply becomes public.
该方法为模块提供了很好的支持,只需要程序员的一点额外工作。它根本不需要前缀。调用导出的和私有函数没有区别。如果程序员忘记local,他也不会污染全局命名空间;只是一个私有函数变为公用的。
What is missing, of course, is access to other modules. Once we make the empty table M our environment, we lose access to all previous global variables. There are several ways to recover this access, each with its pros and cons.
遗漏了什么?当然是访问其他模块。一旦我们把空表M作为我们的环境,就失去了对所有之前的全局变量的访问。有几种方法来恢复该访问,每种都有正反两面。
The simplest solution is inheritance, as we saw earlier:
最简单的解决方案是继承,正如我们较早看到的:
local modname = ...
local M = {}
_G[modname] = M
package.loaded[modname] = M
setmetatable(M, {__index = _G})
setfenv(1, M)
----------------------- Page 8-----------------------144
(You must call setmetatable before calling setfenv; can you tell why?) With this construction, the module has direct access to any global identifier, paying a small overhead for each access. A funny consequence of this solution is that, conceptually, your module now contains all global variables. For instance, someone using your module may call the standard sine function by writing complex.math.sin(x). (Perl's package system has this peculiarity, too.)
(你必须在调用setfenv以前调用setmetatable;你知道原因么?)通过这种构造,模块具有任意全局标识符的直接访问,同时为每次访问花费很小的开销。该解决方案的一个有趣的结果是,你的模块现在概念上含有所有全局变量。例如,某人利用你得模块可通过书写complex.math.sin(x)访问标准正弦函数。(Perl的包系统也有这个特性。)
Another quick method of accessing other modules is to declare a local that holds the old environment:
另一个访问其他模块的快速方法是声明一个持有原环境的局部变量:
local modname = ...
local M = {}
_G[modname] = M
package.loaded[modname] = M
local _G = _G
setfenv(1, M)
Now you must prefix any global-variable name with _G., but the access is a little faster, because there is no metamethod involved.
现在你必须给任何全局变量名加前缀_G.,但是访问会稍微快点,因为不涉及元方法。
A more disciplined approach is to declare as locals only the functions you need, or at most the modules you need:
一个更规整的方法是只声明你需要的函数为局部变量,或者最多是你需要的模块:
-- module setup 模块配置
local modname = ...
local M = {}
_G[modname] = M
package.loaded[modname] = M
-- Import Section: 导入部分:
-- declare everything this module needs from outside 声明该模块需要的来自外部的所有东西
local sqrt = math.sqrt
local io = io
-- no more external access after this point 该点之后不再有外部访问
setfenv(1, M)
This technique demands more work, but it documents your module dependencies better. It also results in code that runs faster than code with the previous schemes.
该技术需要更多的工作,但是它更好地文档化了你的模块依赖关系。它也产生了比以前的方案更快的代码。
阅读(817) | 评论(0) | 转发(0) |