lua 表
最近在尝试配置 awesome WM,因此粗略地学习了一下 lua 。 在学习过程中,我完全被表表在 lua 中的应用所镇住了。
表在 lua 中真的是无处不在:首先,它可以作为字典和数组来用;此外,它还可以被用于设置闭包环境、模块;甚至可以用来模拟对象和类。
字典
表最基础的作用就是当成字典来用。 它的键可以是除了 nil
之外的任何类型的值。
t={}
t[{}] = "table" -- key 可以是表
t[1] = "int" -- key 可以是整数
t[1.1] = "double" -- key 可以是小数
t[function () end] = "function" -- key 可以是函数
t[true] = "Boolean" -- key 可以是布尔值
t["abc"] = "String" -- key 可以是字符串
t[io.stdout] = "userdata" -- key 可以是userdata
t[coroutine.create(function () end)] = "Thread" -- key可以是thread
当把表当成字典来用时,可以使用 pairs
函数来进行遍历。
for k,v in pairs(t) do
print(k,"->",v)
end
运行结果为:
1 -> int
1.1 -> double
thread: 0x220bb08 -> Thread
table: 0x220b670 -> table
abc -> String
file (0x7f34a81ef5c0) -> userdata
function: 0x220b340 -> function
true -> Boolean
从结果中你还可以发现,使用 pairs
进行遍历时的顺序是随机的,事实上相同的语句执行多次得到的结果是不一样的。
表 中的键最常见的两种类型就是整数型和字符串类型。 当键为字符串时,表 可以当成结构体来用。同时形如 t["field"]
这种形式的写法可以简写成 t.field
这种形式。
数组
当键为整数时,表 就可以当成数组来用。而且这个数组是一个 索引从 1 开始 、没有固定长度、可以根据需要自动增长的数组。
a = {}
for i=0,5 do -- 注意,这里故意写成了i从0开始
a[i] = 0
end
当将表当成数组来用时,可以通过长度操作符 #
来获取数组的长度:
print(#a)
结果为:
5
你会发现, lua 认为数组 a
中只有 5 个元素,到底是哪 5 个元素呢?我们可以使用使用 ipairs
对数组进行遍历:
for i,v in ipairs(a) do
print(i,v)
end
结果为:
1 0
2 0
3 0
4 0
5 0
从结果中你会发现 a
的 0 号索引并不认为是数组中的一个元素,从而也验证了 lua 中的数组是从 1 开始索引的。
另外,将表当成数组来用时,一定要注意索引不连贯的情况,这种情况下 #
计算长度时会变得很诡异。
a = {}
for i=1,5 do
a[i] = 0
end
a[8] = 0 -- 虽然索引不连贯,但长度是以最大索引为准
print(#a)
a[100] = 0 -- 索引不连贯,而且长度不再以最大索引为准了
print(#a)
结果为:
8
8
而使用 ipairs
对数组进行遍历时,只会从 1 遍历到索引中断处。
for i,v in ipairs(a) do
print(i,v)
end
结果为:
1 0
2 0
3 0
4 0
5 0
环境(命名空间)
lua 将所有的全局变量/局部变量保存在一个常规表中,这个表一般被称为全局或者某个函数(闭包)的环境。
为了方便,lua 在创建最初的全局环境时,使用全局变量 _G
来引用这个全局环境。因此,在未手工设置环境的情况下,可以使用 -G[varname]
来存取全局变量的值。
for k,v in pairs(_G) do
print(k,"->",v)
end
rawequal -> function: 0x41c2a0
require -> function: 0x1ea4e70
_VERSION -> Lua 5.3
debug -> table: 0x1ea8ad0
string -> table: 0x1ea74b0
xpcall -> function: 0x41c720
select -> function: 0x41bea0
package -> table: 0x1ea4820
assert -> function: 0x41cc50
pcall -> function: 0x41cd10
next -> function: 0x41c450
tostring -> function: 0x41be70
_G -> table: 0x1ea2b80
coroutine -> table: 0x1ea4ee0
unpack -> function: 0x424fa0
loadstring -> function: 0x41ca00
setmetatable -> function: 0x41c7e0
rawlen -> function: 0x41c250
bit32 -> table: 0x1ea8fc0
utf8 -> table: 0x1ea8650
math -> table: 0x1ea7770
collectgarbage -> function: 0x41c650
rawset -> function: 0x41c1b0
os -> table: 0x1ea6840
pairs -> function: 0x41c950
arg -> table: 0x1ea9450
table -> table: 0x1ea5130
tonumber -> function: 0x41bf40
io -> table: 0x1ea5430
loadfile -> function: 0x41cb10
error -> function: 0x41c5c0
load -> function: 0x41ca00
print -> function: 0x41c2e0
dofile -> function: 0x41cbd0
rawget -> function: 0x41c200
type -> function: 0x41be10
getmetatable -> function: 0x41cb80
module -> function: 0x1ea4e00
ipairs -> function: 0x41c970
从 lua 5.2 开始,可以通过修改 _ENV
这个值(lua 5.1 中的 setfenv
从 5.2 开始被废除)来设置某个函数的环境,从而让这个函数中的执行语句在一个新的环境中查找全局变量的值。
a=1 -- 全局变量中a=1
local env={a=10,print=_G.print} -- 新环境中a=10,并且确保能访问到全局的print函数
function f1()
local _ENV=env
print("in f1:a=",a)
a=a*10 -- 修改的是新环境中的a值
end
f1()
print("globally:a=",a)
print("env.a=",env.a)
in f1:a= 10
globally:a= 1
env.a= 100
另外,新创建的闭包都继承了创建它的函数的环境。
模块
lua 中的模块也是通过返回一个表来供模块使用者来使用的。 这个表中包含的是模块中所导出的所有东西,包括函数和常量。
定义模块的一般模板为:
module(模块名, package.seeall)
其中 module(模块名)
的作用类似于:
local modname = 模块名
local M = {} -- M即为存放模块所有函数及常数的table
_G[modname] = M
package.loaded[modname] = M
setmetatable(M,{__index=_G}) -- package.seeall可以使全局环境_G对当前环境可见
local _ENV = M -- 设置当前的运行环境为 M,这样后续所有代码都不需要限定模块名了,所定义的所有函数自动变成M的成员
<函数定义以及常量定义>
return M -- module函数会帮你返回module table,而无需手工返回
对象
lua 中之所以可以把表当成对象来用是因为:
函数在 lua 中是一类值,你可以直接存取表中的函数值。 这使得一个表既可以有自己的状态,也可以有自己的行为:
Account = {balance = 0}
function Account.withdraw(v)
Account.balance = Account.balance - v
end
lua 支持闭包,这个特性可以用来模拟对象的私有成员变量:
function new_account(b)
local balance = b
return {withdraw = function (v) balance = balance -v end,
get_balance = function () return balance end
}
end
a1 = new_account(1000)
a1.withdraw(10)
print(a1.get_balance())
990
不过,上面第一种定义对象的方法有一个缺陷,那就是方法与 Account
这个名称绑定死了。 也就是说,这个对象的名称必须为 Accout
否则就会出错。
a = Account
Account = nil
a.withdraw(10) -- 会报错,因为Accout.balance不再存在
为了解决这个问题,我们可以给 withdraw
方法多一个参数用于指向对象本身。
Account = {balance=100}
function Account.withdraw(self,v)
self.balance = self.balance - v
end
a = Account
Account = nil
a.withdraw(a,10) -- 没问题,这个时候 self 指向的是a,因此会去寻找 a.balance
print(a.balance)
90
不过由于第一个参数 self
几乎总是指向调用方法的对象本身,因此 lua 提供了一种语法糖形式 object:method(...)
用于隐藏 self
参数的定义及传递。这里冒号的作用有两个,其在定义函数时往函数中地一个参数的位置添加一个额外的隐藏参数 sef
, 而在调用时传递一个额外的隐藏参数 self
到地一个参数位置。 即 function object:method(v) end
等价于 function object.method(self,v) end
, object:method(v)
等价于 object.method(object,v)
。
类
当涉及到类和继承时,就要用到元表和元方法了。事实上,对于 lua 来说,对象和类并不存在一个严格的划分。
当一个对象被另一个表的 __index
元方法所引用时,表就能引用该对象中所定义的方法,因此也就可以理解为对象变成了表的类。
类定义的一般模板为:
function 类名:new(o)
o = o or {}
setmetatable(o,{__index = self})
return o
end
或者:
function 类名:new(o)
o = o or {}
setmetatable(o,self)
self.__index = self
return o
end
相比之下,第二种写法可以多省略一个表。
另外有一点我觉得有必要说明的就是 lua 中的元方法是在元表中定义的,而不是对象本身定义的,这一点跟其他面向对象的语言比较不同。
lua 表的更多相关文章
- LUA表克隆方法归纳
lua表克隆 将lua一个表, 克隆出一份为一个独立的另外一个表. 对于一个module, 如果在require之后,获得的表对象, 不能直接修改, 例如lua缓存此表, 但是多次逻辑执行, 都使用的 ...
- <4>Lua表
lua表 1: lua没有数组,但是表可以代替数组的功能(数组部分与非数组部分); 开始的, 1, 2, 3 ...称连续的索引; b.Lua表的连续索引的长度(数组部分); #表的名字; --数组 ...
- Lua表(table)的用法_个人总结
Lua表(table)的用法_个人总结 1.表的创建及表的介绍 --table 是lua的一种数据结构用来帮助我们创建不同的数据类型.如:数组和字典--lua table 使用关联型数组,你可以用任意 ...
- lua表排序
对于lua的table排序问题,一般的按照value值来排序,使用table.sort( needSortTable , func)即可(可以根据自己的需要重写func,否则会根据默认来:默认的情形之 ...
- 对lua表中数据按一定格式处理,循环
function putStartCard(handCard) function dataDeal(array,a,b,c) cclog("进入datadeal=============== ...
- lua表类型
Lua的表的定义: typedef struct Table { CommonHeader; lu_byte flags; lu_byte lsizenode; /* log2 of size of ...
- LUA表与函数的深入理解
local heroInfo = {} --直接打印 table的名字,就会输出该table的内存地址 print("表地址---------------",heroInfo) - ...
- LUA表的引用理解
--lua中引用类型都是分配在堆上的 --因此,我们在使用LUA的table时,可尽可能的使用表的引用,而不需要拷贝表里的元素 --比如,通过RPC协议传来一个表A,我们想要缓存这个表,只需要保存该表 ...
- Lua表(table)的个人总结
1.表的简介和构造 table是个很强大且神奇的东西,又可以作为数组和字典,又可以当作对象,设置module.它是由数组和哈希表结合的实现的.他的key可以是除nil以外任意类型的值,key为整数时, ...
随机推荐
- ceph luminous版本的安装部署
1. 前期准备 本次安装环境为: ceph1(集群命令分发管控,提供磁盘服务集群) CentOs7.5 10.160.20.28 ceph2(提供磁盘服务集群) CentOs7.5 10. ...
- DOM-BOM-EVENT(2)
2.获取DOM元素的方法 2.1.getElement系列 documentElementById 通过id获取元素 <div id="box"></div> ...
- 单调队列练习题解(切蛋糕&好消息,坏消息)
单调队列的练习题解 前言: 在上一篇学习记录中,单调队列给出了几道练习题,因为这两道题的算法以及思路相差无几(几乎可以算是双倍经验quq),所以就在这里集中写一下相关的题解 前置知识: 见:队列专题( ...
- 手写SpringMVC框架(一)-------项目搭建
SpringMVC处理请求的大致流程: 我们来开始着手手写一个SpringMVC框架. 新建一个springMVC项目,流程参见 SpringMVC框架搭建流程 引入servlet相关的jar包: & ...
- NLP(一)
“自然语言处理”(Natural Language Processing 简称 NLP)包含所有用计算机对自然语言进行的操作. 自然语言工具包(NLTK) 语言处理任务与相应 NLTK 模块以及功能描 ...
- java语言基础(七)_继承_super_this_抽象类
继承 1. 继承概述 2. 继承格式 在继承的关系中,"子类就是一个父类".也就是说,子类可以被当做父类看待. 例如父类是员工,子类是讲师,那么"讲师就是一个员工&quo ...
- 【Python】any() 或者 or
前言 在我之前的文章中有any()和all()的对比:any()和all()对比其中介绍了any()函数的基本特性---可迭代对象中有任意一个不为False的时候,返回True,如果可迭代对象为空的话 ...
- HDU 2236 无题Ⅱ
HDU 2236 无题Ⅱ 题目大意 这是一个简单的游戏,在一个\(n*n\)的矩阵中,找n个数使得这n个数都在不同的行和列里并且要求这n个数中的最大值和最小值的差值最小. solution 暴枚\(i ...
- 棋子游戏 51Nod - 1534 思维题
题目描述 波雷卡普和瓦西里喜欢简单的逻辑游戏.今天他们玩了一个游戏,这个游戏在一个很大的棋盘上进行,他们每个人有一个棋子.他们轮流移动自己的棋子,波雷卡普先开始.每一步移动中,波雷卡普可以将他的棋子从 ...
- List集合的遍历方式
遍历List集合的三种方法 List list = new ArrayList(); list.add("aaa"); list.add("bbb"); lis ...