lua基础语法2(table,模块等)

转载自:lua程序设计

table:
Lua也是通过table来解决模块(module)、包(package)和对象(Object)的。 例如string.format表示使用"format"来索引table string。
table 是 Lua 的一种数据结构用来帮助我们创建不同的数据类型,如:数组、字典等。
Lua table 使用关联型数组,你可以用任意类型的值来作数组的索引,但这个值不能是 nil。
Lua table 是不固定大小的,你可以根据自己需要进行扩容。
table 最简单的构造函数是{}
mytable = {} mytable = nil -- lua 垃圾回收会释放内存
当我们获取 table 的长度的时候无论是使用 # 还是 table.getn 其都会在索引中断的地方停止计数,而导致无法正确取得 table 的长度。
arr = {[1]=1,[2]=2,[5]=3,[6]=4} print(#arr) --打印2
可以使用pairs迭代来获得。

Lua 模块与包:
模块类似于一个封装库,把一些公用的代码放在一个文件里,以 API 接口的形式在其他地方调用,有利于代码的重用和降低代码耦合度。当使用require"xxx"时,更像是执行xxx文件的代码,因为没有导入,代码就不会到内存中去。
Lua 的模块是由变量、函数等已知元素组成的 table,因此创建一个模块很简单,就是创建一个 table,然后把需要导出的常量、函数放入其中,最后返回这个 table 就行。
代码如下:
test.lua文件:
module_test = {}
module_test.constA = "ni hao lua"
function module_test.fun()
  print("i am fun")
end
--一个私有函数,不能从外部访问模块这个私有函数,必须通过模块里的公有函数来调用.
--如果为局部函数,必须写在需要调用的它的全局函数上面,否则会报错。
local function localFun()
  print("i am lcoal fun")
end
function module_test.fun3()
    localFun()
end
--全局函数,存放在_G表中
function fun4()
  print("i am fun4")
end
fun4() --必须写返回之前,函数定义之后
return module_test

calltest.lua文件:
local testModule = require "test"
module_test.fun() --打印为:先打印"i am fun4",即先调用了fun4(),然后打印"i am fun"
--testModule.fun4()--报错,无法调用到fun4(),attempt to call field 'fun4' (a nil value)

加载机制:
require 用于搜索 Lua 文件的路径是存放在全局变量 package.path 中,当 Lua 启动后,会以环境变量 LUA_PATH 的值来初始这个环境变量。如果没有找到该环境变量,则使用一个编译时定义的默认路径来初始化。
如果找过目标文件,则会调用 package.loadfile 来加载模块。否则,就会去找 C 程序库。
搜索的文件路径是从全局变量 package.cpath 获取,而这个变量则是通过环境变量 LUA_CPATH 来初始。
搜索的策略跟上面的一样,只不过现在换成搜索的是 so 或 dll 类型的文件。如果找得到,那么 require 就会通过 package.loadlib 来加载它。
http://www.runoob.com/lua/lua-modules-packages.html 设置环境变量,加载C的库
加载C的包:
--C库的路径
local path = "/usr/local/lua/lib/libluasocket.so"
--loadlib函数加载指定的库并且连接到Lua,然而它并不打开库(也就是说没有调用初始化函数),反之他返回初始化函数作为Lua的一个函数,这样我们就可以直接在Lua中调用他。
local f = loadlib(path, "luaopen_socket")
local f2 = assert(f) -- 真正打开库

lua元表:
在 Lua table 中我们可以访问对应的key来得到value值,但是却无法对两个 table 进行操作,比如:做加法。
因此 Lua 提供了元表(Metatable),允许我们改变table的行为,每个行为关联了对应的元方法。
当Lua试图对两个表进行相加时,先检查两者之一是否有元表,之后检查是否有一个叫"__add"的字段,若找到,则调用对应的值。"__add",其对应的值(往往是一个函数或是table)就是"元方法"。
有两个很重要的函数来处理元表:
setmetatable(table,metatable): 对指定table设置元表(metatable),如果元表(metatable)中存在__metatable键值,setmetatable会失败 。
getmetatable(table): 返回对象的元表(metatable)。
__index 元方法:
这是 metatable 最常用的键。
Lua查找一个表元素时的规则,其实就是如下3个步骤:
1.在表中查找,如果找到,返回该元素,找不到则继续
2.判断该表是否有元表,如果没有元表,返回nil,有元表则继续。
3.判断元表有没有__index方法,如果__index方法为nil,则返回nil;如果__index方法是一个表,则重复1、2、3;如果__index方法是一个函数,则返回该函数的返回值。
代码如下:
__index为表时:
myTable = setmetatable({myKey="myKeyValue"},{__index={metaTableValue="metaValue"}})
print(myTable.myKey,myTable.metaTableValue,myTable.xxx) --打印为:myKeyValue metaValue nil
__index为函数时:
myTable = setmetatable({myKey="myKeyValue"},{__index=
    function(myTable,testKey) --参数为:元表的表,元表的表的key值(这个变量可以任意写)
      if testKey == "metaTableValue" then return "metaValue" end
    end})
print(myTable.myKey,myTable.metaTableValue,myTable.xxx)

__newindex 元方法:
__newindex 元方法用来对表更新,__index则用来对表访问 。
当你给表的一个缺少的索引赋值,解释器就会查找__newindex 元方法:如果不存在,就直接赋值,如果存在则调用这个函数,不进行赋值操作,即:按照你想要的方式来决定新加的键值要如何操作。
newTable = {}
myTable = setmetatable({myKey="myKeyValue"},{__newindex=newTable})
myTable.xxx = "xxx"
--打印如下:myKeyValue nil nil xxx
--myTable没办法拿到元表里__newindex的值。
print(myTable.myKey,myTable.xxx,newTable.myKey,newTable.xxx)

如果表没有设置__newindex 元方法,操作都是正常的:
myTable = setmetatable({myKey="myKeyValue"},{__index={metaTableValue="metaValue"}})
myTable.xxx = "xxx"
print(myTable.myKey,myTable.xxx) --打印如下:myKeyValue xxx 

如果使用rawset 函数来更新表就可以拿到元表里__newindex的值:
newTable = {}
myTable = setmetatable({myKey="myKeyValue"},{__newindex=
    function(myTable,key,value)
      rawset(myTable, key, "\""..value.."\"")
      end
    })
myTable.xxx = "xxx"
--打印如下:myKeyValue    "xxx"    nil    nil
print(myTable.myKey,myTable.xxx,newTable.myKey,newTable.xxx)

__call 元方法:
__call 元方法在表调用一个值时调用,比如:myTable(xxx),不是myTable.xx这种调用。
代码如下:
myTable = setmetatable({myKey="myKeyValue"},{__call=function(myTable,xx) 
      print("i am cell "..xx)
      end})
print(myTable(100)) --打印i am cell 100 和 换行空

__tostring 元方法
__tostring 元方法用于修改表的输出行为。
代码如下:
myTable = setmetatable({myKey="myKeyValue"},{__tostring=
    function(myTable) 
      return "i am table , my dizhi is "
    end})
print(myTable) --"i am table , my dizhi is "原本是打印表的地址的

为表添加操作符:
加法:
myTable = setmetatable({myKey="myKeyValue"},{__add=function(myTable,testTable)
      for i=1,#testTable do
        table.insert(myTable,testTable[i])
      end 
      --设置__index与__newindex时,设置的是元方法,
      --但现在是对表的操作,所以必须表相加之后的表返回回去。否则执行加法后表会变成nil
      return myTable 
      end})
aTestTable = {1,2,3}
myTable = myTable + aTestTable
for k,v in pairs(myTable) do
  print(k,v)
end

总结就是:元方法返回一个经过处理的值,操作符返回一个操作后的表。

Lua 中面向对象:
创建对象:
myTable = {}
function myTable:new(obj)
    obj = obj or {}
    setmetatable(obj,self)
    self.__index = self
    return obj
end
aTestVar = "aTestVar"
function myTable:fun()
    print("i am myTable fun")
end
myObject = myTable:new()
myObject:fun() --打印"i am myTable fun"

Lua 继承:
Fruit = {}
function Fruit:new(obj)
    obj = obj or {}
    setmetatable(obj,self)
    self.__index = self
    return obj
end

function Fruit:fun()
    print("i am Fruit fun")
end

--创建一个空表,该空表的元表是Fruit表,将空表赋值给Apple
Apple = Fruit:new()
Apple:fun()--打印:i am Fruit fun

function Apple:new(obj)
  --2种写法等效,这种写法表示,创建一个空表,该空表的元表是Fruit表,然后重新设置元表为Apple
  --obj = obj or Fruit:new(obj)
  --2种写法等效,这种写法表示,该空表的元表是Apple表,而Apple表的元表是Fruit表
  obj = obj or {}
  setmetatable(obj,self)
  self.__index = self
  return obj
end
function Apple:fun() --函数重写,先找Apple表,再找Fruit表
    print("i am Apple fun")
end

Apple_obj = Apple:new()
Apple_obj:fun() --打印:i am Apple fun

_G:
https://blog.csdn.net/a_little_a_day/article/details/79171662 Lua _G
在Lua中,要声明全局变量很简单,那就是定义变量的时候,前面不要加上local。
这个神秘的全局变量,其实本质上也是一个table,它把我们创建的全局变量都保存到一个table里了。
而这个table的名字是:_G
在Lua脚本层,Lua将所有的全局变量保存在一个常规的table中,这个table被称为全局环境,并且将这个table保存在一个全局变量_G中,也就是说在脚本中可以用_G获取这个全局table,并且有_G._G == _G,在默认情况,Lua在全局环境_G中添加了标准库比如math、函数比如pairs等。
比如:
test = 111 --定义了一个全局变量test,于是这个test成为了_G的一个字段。
print(_G["test"])
print(_G.test)

改变函数的全局变量环境的函数:setfenv
对于全局变量,不管到了哪个地方,哪种语言,大家总是会告诫说:“不要滥用,后果自负”。也许是因为这样,所以Lua有了一种比较特殊的机制:非全局环境。我称它为“不会造成全局影响的全局变量”。
setfenv函数两个参数分别代表:
1). 第一个参数,可以是即将要改变环境的函数,也可以是一个数字。数字1代表当前函数,数字2代表调用当前函数的函数,后面以此类推。
2).第二个参数,新的全局环境table。
只要在定义新的环境时,把_G作为一个字段放到新的table里,就可以调用原来的全局变量了。
也可以使用__index元方法保留原来的_G。
代码如下:
AppleNum = 111 
local myTable = {}
setmetatable(myTable,{__index=_G})
-- 将当前全局环境重新设置为新的table,类型于默认在变量前加了myTable.,对于函数也是一样。
setfenv(1,myTable)
AppleNum = 222
function setValue()
  AppleNum = 100
  print("set value") --如果没有将__index=_G,这个print函数是取不到的
end
print(AppleNum) --打印222,设置了新的全局环境之后,新的全局环境不存在原来的AppleNum
print(_G.AppleNum) --打印111,新的全局环境之前的全局环境存在AppleNum变量,且值为111


Lua中的模块与module函数:
http://wiki.jikexueyuan.com/project/openresty-best-practice/not-use-module.html 抵制使用 module()函数来定义 Lua 模块
https://blog.csdn.net/boshuzhang/article/details/74332001  Lua中的模块与module函数

require用于使用模块,module用于创建模块。一个模块就是一个程序库,可以通过require来加载。然后便得到了一个全局变量,表示一个table。这个table就像是一个命名空间,其内容就是模块中导出的所有东西,比如函数和常量。如果表为局部变量还应使require返回这个table。

1. require函数:
    require函数的调用形式为require "模块名"。该调用会返回一个由模块函数组成的table,并且还会定义一个包含该table的全局变量。在使用Lua中的标准库时可以不用显示的调用require,因为Lua已经预先加载了他们。
local myTable = {}
return myTable --像这种写法是一定要返回表的,因为是一个局部表,require之后局部表就没有了,所以要返回,写成这样:
local myTable = require "test"


2,编写模块:
由于在默认情况下,module不提供访问外部的能力,必须在调用它之前,为需要访问的外部函数或模块声明适当的局部变量。Lua提供了一种更为方便的实现方式,即在调用module函数时,多传入一个package.seeall的参数,相当于 setmetatable(M, {__index = _G}) .
把模块里的全局环境设置为M,于是,我们直接定义函数的时候,不需要再带M前缀。
因为此时的全局环境就是M,不带前缀去定义变量,就是全局变量,这时的全局变量是保存在M里。
代码如下:
testOther.lua
--这种写法,即使忘记写local关键字也不会污染全局名称空间,它只是将一个私有函数变成公有了而已。
 --模块名与文件名必须一致,都在会报错。模块名为testOther,文件名为testOther.lua
local moduleName = "testOther" 
local M = {}
_G[moduleName] = M
package.loaded[moduleName] = M --这个应该是对应require
--方法1:获取到外部模块,每次都需要小的开销
--setmetatable(M,{__index=_G})
--方法2:获取到外部模块,相对于方法2快一点,不过之后的全局变量都需要使用前缀_G.比如_G.print("add"),
--要写在setfenv(1,M)之前。
local _G =_G 
--方法3:在setfenv(1,M)之前,将需要用到的函数和模块声明为局部变量。这种方法更加规范,更能清晰的说明模块的依赖性,速度也更快。
--local io = io
--local sqrt = math.sqrt
setfenv(1,M)
M.applenum = 100
function add()
  _G.print("add")
end
调用:
local M = require "testOther"--导入模块之后,返回表,否则需要_G["testOther"].add()
M.add()
print(M.applenum)
-------------------------
test123.lua
--module默认不提供外部访问,你可以使用上面testOther里的第2,3种方法(好像都不太行的样子??)。
--package.seeall这种写法则是默认写了上面testOther方法1中的setmetatable(M,{__index=_G})

module("test123",package.seeall) --与上面一样,必须与文件同名
applenum = 100
function add()
    print("add")
end
调用:
local M = require "test123"--导入模块之后,返回表,否则需要_G["test123"].add()
M.add()
print(M.applenum)

testOther.lua和test123.lua这2个是等价的。
 

 

 

 

 

 

 

 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值