lua学习04——环境&模块

***************************************转载请注明出处:http://blog.csdn.net/lttree********************************************




本篇文章主要内容:

> lua的环境

> lua的全局变量

> lua的非全局变量

> lua的模块与包

> require

> 编写模块的基本方法




1.环境
lua 将它所有的全局变量保存在一个常规的table中,这个table称为——“环境”,
这样做的优点有两个——1. 不需要再为全局变量创造一种新的数据结构,简化了lua内部的实现(毕竟是一门脚本语言)
2. 可以像其他的table一样操作这个table(充分利用table强大的功能)
lua将 环境table保存在一个 全局变量—— _G 中。

你可以 用下面这个语句,看看,全局变量都有什么东西:

for n in pairs(_G) do print(n) end




2.全局变量

刚开始,你一定写过 

a = 3
print(a)

这种东西,实际上,这是在全局中声明定义了一个变量,并去使用了它。

在lua中,全局变量,不需要声明就可以直接去使用,这样看起来非常方便,但是往往会导致巨大问题,尤其是在大程序中,难以发现。
这时候,我们就需要设置一些东西,提醒一下,

上面说过,全局变量是个table,在table查询、更新 表时,需要访问元表,据此可以做一个机制:

setmetatable(_G, {
	__newindex = function(_, k)
		error("Attempt to write to undeclared variable "..k)
	end,
	__index = function(_, k)
		error("Attempt to read undeclared variable "..k)
	end
})
print(num)

当然,这里也有办法可以绕过它,之前说过的 rawset & rawget

如果你想写死,也可以,看之前的博客,上面有写过可读table的实现。



3.非全局变量
  关于 环境 有一个很大的问题,因为它是全局的,任何对它的修改都会影响程序的所有部分。
  在lua5中,对这个问题进行了一些改进,它允许每个函数拥有一个自己的环境来查找全局变量。
  一般通过函数 setfenv 来改变一个函数的环境(注:setfenv在 lua 5.2版本中已被废除,但这里也会介绍一下)
  setfenv有两个参数,第一个参数表示:当前函数调用栈的层数(比如,1表示当前函数,2表示调用当前函数的函数...)
  第二个参数 就是新环境的table,

  或许,读到这,你就可能去实践了:

 a = 1
 setfenv(1, {})
 print(a)

会出错——试图调用一个全局变量 print(一个空值)

 吓!! 什么情况,print不应该是 全局变量_G自带的吗?
 其实,描述中给的很清楚,第二个参数表示新环境的table,就是在新环境中的全局变量(有点绕,多读几遍)
 新的table中并没有 _G,所以会出错了,

 我们需要记录下来原来的全局变量:

a = 1
 setfenv(1, {_G = _G})
 _G.print(a)
 _G.print(_G.a)

很别扭把,再次想起 table,当我们在当前table找不到字段,让它去之前的全局变量中找啊~

a = 1
 local newgt = {}
 setmetatable(newgt, {__index = _G})
 setfenv(1, newgt)
 print(a)

现在,顺眼很多了吧。




4.lua的模块与包

  通常,lua不会设置规则,但是它会提供许多强力的机制来让开发者去设置规则。
  Lua的5.1版本开始,为模块和包定义了一系列规则,主要是 require(用于使用模块) 和 module(用于创建模块)
  从用户的观点来看,一个模块就是一个程序库,可以通过require来加载,然后得到一个全局变量,表示一个table。
  这个table就像是一个名称空间,其内容就是模块中导出的所有东西,例如函数和常亮。一个规范的模块还应该让require返回这个table。
  使用table来实现模块的优点在于,可以像操作普通table一样来操作模块,并且能利用lua现有的功能来实现各种额外功能。

  如果有用户要调用模块中的某个函数,最简单的方法是:

require "modu"
modu.foo()

如果,模块名字较长,没关系,给它一个局部名称

local m = require "modu"
m.foo()

当然,也可以给函数一个别名

require "modu"
local f = modu.foo
f()




5.require
require用来加载模块,只需要简单的 require "模块名"
该调用会返回一个由模块函数组成的table,并且还会定义一个包含该table的全局变量。然而,这些行为都是由模块完成的,并不是require去做。

require的行为:

function require (name)
 	-- 先检查模块是否加载
	if not pachage.loaded[name] then
		local loader = findloader(name)
		if loader == nil then
			error("unable to load module "..name)
		end
		-- 标记 模块已加载
		package.loaded[name] = true
		-- 初始化模块
		load res = loader(name)
		if res ~= nil then
			package.loaded[name] = res
		end
	end
	return package.loaded[name]
 end

 它先去在 table package.loaded中检查 模块是否已加载。如果已加载,直接返回相应的值。

   因此,只要模块加载过,后续的require调用都会返回同一个值,不会再次去加载它。

   如果模块没加载,就会去为这个模块找一个加载器(loader),先在package.preload中查询传入的模块名。如果在其中找到函数就以该函数作为模块的加载器。

   通过preload table,就有了一种通用的方法来处理各种不同的情况。通常这个table中不会找到有关指定模块的条目,那么require就会尝试从lua文件或C程序库中加载模块。

   如果 require找到了一个lua文件,就用loadfile来加载;如果 找到了C程序库,就用loadlib来加载。(但这两种,都仅仅是加

载,并没有运行代码)

   在上面的代码中,在调用加载器前,require先将true赋予package.loaded中对应字段,一次将模块标记为加载,这是非常必要的,

   因为如果 A需要requireB,而在B中又require到A,就会导致无限循环。

   之前有说过,require对同一个库,只加载一次,如果非要多次加载,就可以将package.loaded中的相应模块条目进行删除:

package.laoded["相应模块"] = nil
require "相应模块"

  require用于搜索lua文件的路径存放在变量package.path中。当lua启动后,便以环境变量LUA_PATH的值来初始化这个变量。如果没有找到这个变量,就使用一个编译时定义的默认路径来初始化。

   在使用LUA_PATH时,lua会将其中所有的子串";;"替换成默认路径。

   比如,路径为: mydir/?.lua;;  那么最终的路径就是  mydir/?.lua 并紧随默认路径。

   如果没有相符的lua文件,它就会找C程序库。这类搜索会从变量 package.cpath(相对于 package.path)获取路径。而这个变量则是通过环境变量 LUA_CPATH(相对于LUA_PATH) 来初始化。




6.编写模块的基本方法

 在lua中创建模块嘴简单的方法是,创建一个table,并将所有需要导出的函数放入其中,最后返回这个table。

下面是实现 复数 相关的操作:

<1> 初始版本

complex = {}
 function complex.new(r, i) return {r = r, i = i} end
 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 comlex.inv(c)
 	local n = c.r^2 + c.i^2
 	return complex.new(c.r/n, -c.i/n)
 end


 function complex.div(c1, c2)
 	return complex.mul(c1, inv(c2))
 end


 return complex


<2> 我们可以发现,这里每次调用模块内的函数,它都要加前缀,

但,如果我们要换个模块名,是不是会很崩溃呢?————每个函数名的前缀,调用函数的前缀都要换!!

而且,每次都要return,好麻烦,万一忘了...

所以,做一些改进:

local modname = ...
 local M = {}
 _G[modname] = M
 package.loaded[modname] = M
 <如前>


<3> 还可以更精进一些,每次都要写个前缀,好烦啊~,之前有讲过 setfenv ,可以派上用场了,

当然,全局的变量还是要继承过来的

local modname = ...
 local M = {}
 _G[modname] = M
 package.loaded[modname] = M
 setmetatable(M, {__index = _G})
 setfenv(1, M)


<4> 这样做,很方便,但是,此时 我们的模块包含了全局变量,这是非常不好的。

所以,要用一些正规的手段,比如将需要用到的函数或模块声明为局部变量,然后关掉外部访问

local modname = ...
 local M = {}
 _G[modname] = M
 package.loaded[modname] = M


 -- 接下来,将用到的声明为局部变量:
 local sqrt = math.sqrt
 local io = io


 -- 关闭外部访问
 setfenv(1, M)


虽然,代码复杂了一些,但是可以更干净、清晰,比之前的运行速度要快。


<5> 可以总结上面的这些步骤:

- 从require传入的参数中获取模块名
- 建立一个空的table
- 在全局变量中将这个table添加到模块名对应的字段
- 在table中设置 模块相关东西
- 设置 环境变量

因为这几步都是公共的,所以在lua5.1中,就加入了一个 module函数,来做上面五件事,

But,在lua5.2中,官方建议大家放弃 module/package机制,废弃了 module、setfenv、getfenv。



<6> lua5.2中 废弃了 setfenv 和 getfenv。启用了 语法糖 _ENV
我们来了解一下 _ENV

对于一个 没有绑定任何声明的变量,会被转换为带_ENV前缀的变量,比如:

val = 3
  local v = 5
  print(val)
  print(_ENV.val)
  print(v)
  print(_ENV.v)
  
  -- result
  3
  3
  5
  nil

如果是在函数中:

function fun(val1)
  	val2 = 2
  	local val3 = 3
  
  	print(_ENV.val1)
  	print(_ENV.val2)
  	print(_ENV.val3)
  
  	for val4=1,1 do
  		print(_ENV.val4)
  	end
  end
  
  fun(1)
  
  -- result
  nil
  2
  nil
  nil


_ENV并不是局部变量,它是一个非局部变量,为了兼容5.1之前版本,_ENV._G = _ENV

在5.1版本中,我们可以为一段代码块(or函数)设置环境 —— 通过函数setfenv

但这样,会导致一段代码访问全局变量的时候使用了setfenv制定的table,而不是_G.

在5.2版本中 setfenv被废弃,我们可以通过在函数定以前覆盖_ENV变量来为函数定义设置一个全新的环境。





***************************************转载请注明出处:http://blog.csdn.net/lttree********************************************

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要在Nginx中配置Lua模块,你需要遵循以下步骤: 1. 在安装Nginx之前,请确保你已经安装了LuaJIT或者Lua的开发库。你可以通过在终端中运行以下命令来安装它们: 对于Ubuntu/Debian系统: ``` sudo apt-get install libluajit-5.1-dev ``` 对于CentOS/RHEL系统: ``` sudo yum install lua-devel ``` 2. 下载Nginx的源码,并解压缩它。 3. 在解压缩后的目录中,运行以下命令来配置编译选项,其中`--prefix`是安装目录的路径: ``` ./configure --prefix=/path/to/install --add-module=/path/to/nginx-lua-module ``` 请将`/path/to/nginx-lua-module`替换为你实际安装Lua模块的路径。 4. 运行以下命令编译和安装Nginx: ``` make sudo make install ``` 5. 配置Nginx以使用Lua模块。在Nginx的配置文件(通常是`nginx.conf`)中添加以下内容: ``` http { lua_package_path "/path/to/lua-scripts/?.lua;;"; lua_package_cpath "/path/to/lua-modules/?.so;;"; server { ... location / { ... content_by_lua_file /path/to/lua-script.lua; } } } ``` 请将`/path/to/lua-scripts`替换为你实际存放Lua脚本的路径,将`/path/to/lua-modules`替换为你实际存放Lua模块的路径,将`/path/to/lua-script.lua`替换为你实际的Lua脚本文件路径。 6. 保存并关闭配置文件后,重新启动Nginx服务。你现在应该可以在浏览器中访问配置的Lua脚本了。 这些步骤涵盖了在Nginx中配置Lua模块的基本过程。请根据你自己的需求进行相应的调整和扩展。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值