现在的位置: 首页 > 综合 > 正文

module

2019年06月16日 ⁄ 综合 ⁄ 共 2192字 ⁄ 字号 评论关闭

2008年12月9日

今天下午调试程序的时候,遇到了一件非常奇怪的事。弄清楚了之后,才发现原来是Lua中Module中自有环境的问题。

大体情况是这样的,我在主程序中设定的全局变量,在模块文件中可以访问到,并修改了这个全局变量的值,但是在模块调用返回后,再次使用这个全局变量的值,发现它没有被赋值,没有被改变。究其原因,发现是因为Lua的模块里面,采用了自己的全局环境(这个全局环境会将主程序中的全局环境做为备选查找表,即使用__index联系),这与主程序中的全局环境是不一样的(两个不同的表)。模块中如果有一个变量被第一次赋值的话,Lua会认为是在模块的环境中新建一个全局变量,即使这个全局变量与主程序中的全局变量重名,它也不会去引用主程序中的全局变量,然后赋值。


下面举几一个sample来解释这种情况:

主程序文件: test.lua

  1. require "a"
  2. print("================================")
  3. print(GGG[1])
  4. print(GGG[2])
  5. print(SKU[1])
  6. print(SKU[2])
  7. print("================================")
  8. f = require "b"
  9. f.run()
  10. print(GGG[1])
  11. print(GGG[2])
  12. print(SKU[1])
  13. print(SKU[2])
  14. print("================================")

另一个文件: a.lua

  1. GGG = {}
  2. GGG[1] = 10
  3. SKU = {}

模块文件: b.lua

  1. module(..., package.seeall)
  2. function run()
  3.     print("--------------------------------")
  4.     GGG[2] = 20
  5.     print(GGG[1])
  6.     print(GGG[2])
  7.     SKU = GGG
  8.     print(SKU[1])
  9.     print(SKU[2])
  10.     print("--------------------------------")
  11.     return 0
  12. end

运行 lua test.lua 后,结果如下:

  1. ================================
  2. 10
  3. nil
  4. nil
  5. nil
  6. ================================
  7. --------------------------------
  8. 10
  9. 20
  10. 10
  11. 20
  12. --------------------------------
  13. 10
  14. 20
  15. nil
  16. nil
  17. ================================

而如果将模块文件 b.lua 改成如下形式:

  1. module(..., package.seeall)
  2. function run()
  3. print("--------------------------------")
  4. GGG[2] = 20
  5. print(GGG[1])
  6. print(GGG[2])
  7. _G.SKU = GGG
  8. print(SKU[1])
  9. print(SKU[2])
  10. print("--------------------------------")
  11. return 0
  12. end


那么结果如下:

  1. ================================
  2. 10
  3. nil
  4. nil
  5. nil
  6. ================================
  7. --------------------------------
  8. 10
  9. 20
  10. 10
  11. 20
  12. --------------------------------
  13. 10
  14. 20
  15. 10
  16. 20
  17. ================================


可以看到,程序修改之前没有对主程序全局变量进行赋值,而修改之后,给主程序全局变量赋了值。


通过本例,我们还可知道,require 既可用来加载模块文件(包括动态链接库模块),也可用来加载普通的代码块文件。模块文件加载进来后,使用一个引用来调用模块中的方法和变量。而普通的代码块文件在被require调入后,就相当于(如果有Local变量,则不完全是)成了主程序代码的一部分,全局变量可以贯穿使用。(非本地)函数也可以直接调用。

Lua的如下语句:

  1. GGG[2] = 20

实际上进行了两个操作,首先是对GGG的引用,如果在当前模块中找不到它的定义,就会通过__index元方法查找到_G中去,如果在_G中找到了,那么就引用_G中的这个GGG变量,并且给它添加一个元素值20。而如果在_G中也没有找到定义,那程序会认为是第一次碰到GGG这个变量,第一次碰到这个变量就要去引用它,并且是引用它其中的一个元素,便会报错。所以,类似这样的数据元素赋值的操作实际上是:引用加索引和赋值三个操作的结合。

而简单的赋值语句就不一样了:

  1. SKU = GGG

这句,会引用GGG,GGG在当前模块找不到的话,会到_G中去找。它也会引用SKU,但它发现,这是这个模块中的第一次出现的变量,并且被赋值(写),于是会当成是一个新的变量来创建。因此,这里的SKU,并非_G中的那个SKU,而仅仅是模块内部的一个变量。究根揭底,还是因为模块的环境与主环境之间只有__index(读回溯),而没有__newindex(写回溯)的缘故。

抱歉!评论已关闭.