文章目录
1 Lua简介
参考文章
Lua1.x
对于学术用途是免费的,但是商业用途则需要协商
Lua2.x
table作为对象和类的基础,table索引,算术操作符,字符串拼接,顺序比较,函数调用, Lua作为无限制免费软件发布,
Lua3.x
我们解决了fallback冲突问题。我们将fallback替换为tag方法:钩子是以(event, tag)的形式挂在字典里的。
…
- Lua应用场景:游戏开发,独立应用脚本,扩展和数据库插件,安全系统
2 Lua编程工具
Lua在线编程
第一个Lua程序
Lua开发工具LuaEdit下载
lua开发工具安装教程
3 起点
3.1 编程方法
lua [options] [script [args]]
-e:直接将命令传入Lua
prompt> lua -e "print(math.sin(12))" --> -0.53657291800043
-l:加载一个文件.
-i:进入交互模式.
_PROMPT内置变量作为交互模式的提示符
prompt> lua -i -e "_PROMPT=' lua> '"`
lua>
Lua的运行过程,在运行参数之前,Lua会查找环境变量LUA_INIT的值,如果变量存在并且值为@filename,Lua将加载指定文件。如果变量存在但不是以@开头,Lua假定filename为Lua代码文件并且运行他。利用这个特性,我们可以通过配置,灵活的设置交互模式的环境。可以加载包,修改提示符和路径,定义自己的函数,修改或者重命名函数等。
全局变量arg存放Lua的命令行参数。
prompt> lua script a b c
在运行以前,Lua使用所有参数构造arg表。脚本名索引为0,脚本的参数从1开始增加。脚本前面的参数从-1开始减少。
prompt> lua -e "sin=math.sin" script a b
arg表如下:
arg[-3] = “lua”
arg[-2] = “-e”
arg[-1] = “sin=math.sin”
arg[0] = “script”
arg[1] = “a”
arg[2] = “b”
交互式编程,在控制台交互界面直接输入lua代码运行:
脚本式编程,通过调用lua文件,使lua文件中的代码直接运行:
开发工具编程,在Lua开发工具里进行编译,开发工具里可以对代码进行调试,方便上手:
3.2注释
单行注释
-- 单行注释
多行注释
--[[
多行注释
多行注释
]]--
3.3关键字(21个)
and | break | do | else |
---|---|---|---|
elseif | end | false | for |
function | if | in | local |
nil | not | or | repeat |
return | then | true | until |
while |
3.4 标识符
注:lua是大小写敏感的,比如a和A是两个不同的标识符
标识符:用于定义一个变量,函数获取其他用户定义的项目,以字母(a-z或A-Z)或者 下划线_ 开头的字母、下划线、数字序列。
4 数据类型和值
Lua是动态类型语言,变量不要类型定义。变量没有预定义的类型,每一个变量都可能包含人一种类型的值。Lua中有8个基本类型分别是nil,boolean,number,string,userdata,function,thread和table,函数type可以测试指定变量或者值的类型。
一般情况下同一变量代表不同类型的值会造成混乱,最好不要用,但是特殊情况下可以带来便利,比如nil
4.1 nil
一个取值nil;一个全局变量没有被赋值前为nil,给全局变量赋nil可以删除该变量
4.2 boolean
两个取值false和true。在控制结构中除了false和nil为假,其他的值都为真。比如0和空串都是真
4.3 number
表示实数,Lua中没有整数。数据常量的小数部分和指数部分都是可选的,数据常量的例子:
4 0.4 4.57e-3 0.3e12 5e+12
4.4 string
字符的序列,lua是8位字节,所以字符串可以包含任何数值字符,包括嵌入的0。意味着你可以存储任意的二进制数据在一个字符串里。Lua中字符串是不可以修改的,你可以创建一个新的变量存放你要的字符串,如下:
string和其他对象一样,Lua自动进行内存分配和释放,一个string可以只包含一个字母也可以包含一本书,Lua可以高效地处理长字符串,1M的string在Lua中很是常见的。可以使用单引号或者双引号表示字符串,也可以使用[[]]间的字符表示字符串,最好使用一种,除非两种引号嵌套情况,
a = "a line"
b = 'another line'
可以通过#来计算字符串的长度,放在字符串前面
- 转义字符
\a 响铃(BEL)
\b 退格
\f 换页
\n 换行
\r 回车
\t 水平制表tab
\v 垂直指标
\\ 反斜线字符\
\" 双引号字符
\' 单引号字符
\ddd 1到3位八进制数所代表的任意字符
\xhh 1到2位十六进制所代表的任意字符
4.5 table
table的创建是通过构造表达式来完成,最简单的构造表达式是{},用来创建一个空表。
local tb1 = {}
local tb2 = {
"yu","an","rui"}
table其实是一个关联数组,数组的索引可以是数字也可以是字符串(相当于map)
a = {}
a["key1" ]= "v1"
a[1] = 22;
a[1] = a[1]+11;
for k,v in pairs(a) do
print(k,":",v)
end
-- key1:v1
-- 1:33
Lua里表的默认初始索引一般以1开始
local tb2 = {
"yu","an","rui"}
for k,v in pairs(tb2) do
print(k,v)
end
--1yu
--2an
--3rui
4.6 function
函数可以存在变量里
function say(n)
print(n)
end
say2 = say
say2(10)
函数可以以你们函数的方式作为参数传递
function test(t1,fun)
for k,v in pairs(t1) do
print(fun(k,v))
end
end
tab = {
"a","b"}
test(tab,
function(key,val)
return key .. " = ".. val
end
);
4.7 thread
线程和协程的区别:线程可以同时多个运行,而协程任意时刻只能运行一个
- lua里,最主要的线程是协同程序。和线程差不多,拥有自己独立的栈,局部变量和指令指针,可以跟其他协同程序共享全局变量和其他大部分东西。
4.8 userdata
userdata是一种用户自定义数据,用于表示一种由应用程序或c/c++语言库所创建胡类型,可以将任意c/c++的任意数据类型的数据(通常是struct和指针)存储到Lua变量中调用。
5 变量
变量有三种里类型:全局变量,局部变量,表中的域
默认情况下,变量总是认为是全局的,全局变量不需要声明,给一个变量赋值后即创建了全局变量,访问一个没有初始化的全局变量也不会出错,只不过得到的结果是nil。如果你想删除一个全局变量,只需要讲变量赋值为nil。
用local显式声明为局部变量,局部变量的作用域为从声明位置开始到所在语句块结束。变量的默认值为nil。访问局部变量胡速度比全局变量更快
6 表达式(运算符)
- 算术运算符:
+ - * / ^ %
- 关系运算符:
< > <= >= == ~=
- 逻辑运算符:
and or not
- 连接运算符:
..
字符串连接,如果操作数为数字,Lua将数字转成字符串。
print("Hello " .. "World") --> Hello World
print(0 .. 1) --> 01
- 其他运算符:
#
返回字符串或表的长度
print(#"Hello") --->5
- 运算符优先级
从高到低。
^
not - (unary)
* /
+ -
..
< > <= >= ~= ==
and
or
其中除了^和…外所有的二元运算符都是左连接的
x^y^z <--> x^(y^z)
7 基本语法
7.1 赋值语句
- 赋值改变一个变量的值和改变表域的最基本方法
a = "hello" .. "world"
t.n = t.n+1
- 多个变量同时赋值
a,b = 10,2*x <--> a=10; b=2*x
遇到赋值语句Lua先计算右边所有的值然后再执行赋值操作,这样我们就可以进行交换变量胡值
- 多变量赋值,数和值的个数不一致
值个数不足够,补nil;变量个数不足,多余的值会被忽略 - 多值赋值经常用来交换变量,或将函数调用返回给变量
a, b = f() --f()返回两个值,第一个赋给a,第二个赋给b。
7.2 控制结构语句
控制结构的条件表达式结果可以是任何值,Lua认为false和nil为假,其他值为真
- if语句-三种形式
if conditions then
then_part
end
if conditions then
then_part
else
else_part
end
if conditions then
then_part
elseif conditions then
elseif_part
.. -->多个elseif
else
else_part
end
- while语句
while conditon do
statements;
end
- repeat-untile语句
repeat
statements;
until conditons;
- for-两大类语句
第一 ,数值for循环
for var = exp1,exp2,exp3 do
loop-part
end
for将用exp3作为stept从exp1到exp2,执行loop-part.其中exp3可以省略,默认step=1
有几点需要注意:三个表达式只会在循环前计算一次;控制变量var是局部变量自动被声明,并且只能在循环内有效;循环过程中最好不要改变控制变量的值。
第二,范式for循环
iparis一般用来遍历数组从1,2…;paris一般用来遍历表(键值对),它也可以遍历数组,数组实质上就是键为1,2…的,元素为值的表;如果表的元素里从1,2连续开始的,也可以使用ipairs访问1,2…等的键值对。
--print all values of array 'a'
for i,v in iparis(a) do
print(v)
end
--print all keys of table 't'
for k in pairs(t) do
print(k)
end
eg:构造反向表
days = {
"Sunday", "Monday", "Tuesday", "Wednesday",
"Thursday", "Friday", "Saturday"}
revDays = {}
for i,v in ipairs(days) do
revDays[v] = i
end
x = "Tuesday"
print(revDays[x]) --> 3
8 函数
函数的用途:调用语句-完成指定任务;赋值语句的表达式-计算并返回值
语法:
function func_name(arguments-list)
statements-list;
end
调用函数时,如果参数列表为空,必须使用()表明是函数调用。
print(8*9,9/8)
print(os.data())
function show()
print("heiren,are you ?")
end
show();
当函数只有一个参数并且这个参数是字符串或表构造的时候,()可有可无
print "Hello World" <--> print("Hello World")
dofile 'a.lua' <--> dofile ('a.lua')
print [[a multi-line <--> print([[a multi-line
message]] message]])
f{x=10, y=20} <--> f({x=10, y=20})
type{} <--> type({})
-
面向对象方式调用函数的语法,比如o:foo(x)与o.foo(o.x)等价
-
Lua使用的函数,即可是Lua编写的,也可是其他语言编写的,对于Lua程序员,用什么语言实现的函数使用起来都一样。
-
Lua函数实参和形参的匹配与赋值语句类似,多余部分被忽略,缺少部分用nil补足。
-
8.1 多返回值
Lua函数可以返回多个结果值,比如:
s.e = string.find("hello lua users","Lua"); print(s,e) -->7 9
Lua函数中,在return后列出要返回的值的列表即可返回多值,比如:
function test() local arg1,arg2 = 1,3; return arg1,arg2; end print(test()) -->1,3
8.2 可变参数
Lua函数可以接收可变数目的参数,和C语言类似,在函数参数列表中使用三点…表示函数有可变的参数
function add(...)
result = 0
for i,v in ipairs(arg) do
result = result+v;
end
print(#arg,"个数");--6个数
print(result)--21
end
add(1,2,3,4,5,6)
print(result)--21
8.3 命名参数
Lua的函数参数是和位置相关的,调用时实参会按顺序依次传给形参。有时候指定参数是很有用的。比如下面
function rename(arg)
return os.rename(org.old,org.new)
end
- 当函数的参数很多的时候,这种函数参数的传递方式很方便的。比如:传递坐标,颜色等
c = Color{
r = 0,g = 256,b=100
}
9 字符串(string)
9.1 字符串操作
string.upper(argument) --转换成大写字母
string.lower(argument) -- 转换成小写字母
string.gsub(mainString,findString,replaceString) --在字符串中替换
string.reverse(arg) --字符串反转
string.format(...) --返回一个类似printf的格式化的字符串
string.char(arg) string.byte(arg[,int]) --将整型数字转换成字符并连接 转换字符为整型数字
string.len(arg) --计算字符串的长度
string.rep(string,n) --返回字符串str的n个拷贝
.. -- 连接两个字符串 如果其中有一个是nil,会报错
string.gmatch(str,pattern) -- 返回一个在字符串str找到的下一个符号pattern描述的字串,如果没有找到,迭代函数返回nil
string.match(str,pattern,init) --寻找str中的第一个配对,init可选,搜寻的起点,默认为1,没有成功的配对,返回nil
tonumber(str)将字符串转换成数字
9.2 字符串格式化
string.format()
格式字符串包含的转义符:
- %c 接收一个数字,并将其转换为ASCII码表中的对应的字符
- %d,%i 接收要给数字并将其转化为有符号的整数格式
- %o 接收一个数字并将其转换为八进制数格式
- %u 接收一个数字并将其转换为无符号整数格式
- %x 接收一个数字并将其转换为十六进制数格式,小写字母
- %X 接收一个数字并将其转换为十六进制数格式,大写字母
- %e 接收一个数字并将其转换为科学计数格式,使用小写字母e
- %E 接收一个数字并将其转换为科学计数格式,使用大写字母E
- %f 接收一个数字并将其转换为浮点数
- %g(%G) 接收一个数字并将其转换为%e及%f中较短的形式
- %q 接收一个字符串并将其转换为可安全被lua编译器读入的格式
- %s 接收要给字符串并按照给定的参数格式化该字符串
为进一步细化格式,可以在%后添加参数,参数将以如下顺序读入: - 符号:+
- 占位符 :0 默认为空格
- 对齐标识:指定了字串宽度是,默认右对齐,增加-号可以改为左对齐
- 宽度数值
- 小数位数/字串裁切:%5.3f 只显示小数3位,%5.3s只显示字符串前3位等
9.3 字符串模式匹配(pattern)
单个字符(除^$()%.[]*±?外)与该字符自身配对
- .点 与任何字符配对
- %a 与任何字母配对
- %c 与任何控制符配对,比如\n
- %l 与任何小写字母配对
- %d 与任何数字配对
- %p 与任何标点配对
- %s 与空白字符配对
- %u 与任何大写字母配对
- %w 与任何字母/数字配对
- %x 与任何十六进制数配对
- %z 与任何代表0的字符配对
- %x
- [数个字符类] 与任何[]中包含的字符类配对,比如[%w_]与任何字母/数字或下划线配对
- [^数个字符类] 与任何不包含在[]内的字符配对
当上述的字符类用大写书写时,表示与非此字符类的任何字符配对。
10 数据结构
10.1 数组
在Lua中通过整数下标访问table中元素,即是数组。并且数组大小不固定,可以动态增长。通常我们初始化数组时,就间接地定义了数组的大小,例如:
array = {
"lua","Tutorial"}
for i = 1,2 do
print(array[i])
end
--输出
lua
Tutorial
我们可以用构造器在创建数组的同时初始化数组:
array = {
1,3,5,14,24,234};
10.2 矩阵和多维数组
Lua中有两种表示矩阵的方法,一是数组的数组,也就是说,table的每个元素是另一个table;二是将行和列组合起来,如果索引下标都是整数,通过第一个索引乘一个常量(列)再加上第二个索引,如果索引是字符串,可以用一个单子符讲两个字符串索引连接起来
mt = {}
for i = 1,10 do
mt[i] = {}
for j = 1,10 do
mt[i][j] = 0
end
end
mt = {}
for i = 1,10 do
for j = 1,12 do
mt[i*12 + j] = 0
end
end
Lua中table实现的数据本省就具有稀疏性。不管采用那种方式,只需要存储那些非nil的元素,该特性可用于稀疏矩阵中。
10.3 链表
根节点:可以理解为尾节点
`list = nil
在链表表头插入一个值为v的节点:
list = {
next = list,value = v};
遍历链表
local l = list
while l do
print(l.value)
l = l.next
end
其他类型的链表,像双向链表和循环链表类似的也是很容易实现的。
10.4 队列和双向队列
可以使用lua的table库提供的insert和remove操作来实现队列,但这种方式实现的队列针对大数据量时效率太低,有效的方式是使用两个索引下表,一个表示第一个元素,另一个表示最后一个元素。
function ListNew()
return {
first = 0,last = -1}
end
为了避免污染全局命名空间,重写代码
List = {}
function List.new()
return {
first = 0,last = -1}
end
队列的插入和删除
function List.pushleft(list,value)
local first = list.ifrst - 1;
list.first = first
list[first] = value
end
function List.pushright(list,value)
local last = list.last+1
list.last = last
list[last] = value
end
function List.popleft(list)
local first = list.first
if first > list.last then error("list is empty") end
local value = list[first]
list[first] = nil
list.first = first + 1
return value
end
function List.popright(list)
local last= list.last
if first > list.last then error("list is empty") end
local value = list[last]
list[last] = nil
list.last= last+ 1
return value
end
对严格意义上的队列来讲,我们只能调用pushright和popleft,这样一来,first和last的索引值都随之增加,幸运的是我们使用的是Lua的table实现的,你可以访问数组的元素,通过使用下表从1到20,也可以使用16777216到16777236。另外Lua使用双精度表示数字,假定你每秒中执行100万次插入操作,在数值溢出前你的程序可以运行200年。
10.5 集合和包
将所有集合中的元素作为下标存放在一个table里,下面不需要查找table,只需要测试看对于给定的元素,表的对应下标的元素值是否为nil。比如:
reserved = {
["while"] = true,["end"] = true;
["function"] = true,["local"] = true,
}
for w in allwords() do
if reserved[w] then
--'w' is a reserved word
...
end
还可以使用辅助函数更加清晰的构造集合:
function Set(list)
local set = {}
for _,l in ipairs(list) do set[l] = true end
return set
end
reserved = Set{
"while","end","function","local"}
11 迭代器
11.1 泛型for迭代器
泛型for在自己内部实际上保存了迭代函数,状态常量,控制变量。泛型for迭代器提供了集合的key/vlue对,语法格式如下:
for k,v in pairs(t) do
print(k,v)
end
11.2无状态迭代器
无状态迭代器是指一种自身不保存任何状态的迭代器。因此,我们可以在多个循环中使用同意无状态的迭代器来避免创建新闭包的开销。迭代器状态就是需要遍历的table及当前的索引值(控制变量),使用代码重写ipairs,如下:
local function iter(a,i)
i = i+1
local v = a[i]
if v then
return i,v
end
end
function ipairs(a)
return iter,a,0
end
12 table(表)
Table 操作
以下列出了 Table 操作常用的方法:
序号 | 方法 | 用途 |
---|---|---|
1 | table.concat (table [, step [, start [, end]]]): | concat是concatenate(连锁, 连接)的缩写. table.concat()函数列出参数中指定table的数组部分从start位置到end位置的所有元素, 元素间以指定的分隔符(sep)隔开。 |
2 | table.insert (table, [pos,] value); | 在table的数组部分指定位置(pos)插入值为value的一个元素. pos参数可选, 默认为数组部分末尾. |
3 | table.maxn (table) | 指定table中所有正数key值中最大的key值. 如果不存在key值为正数的元素, 则返回0。(Lua5.2之后该方法已经不存在了,本文使用了自定义函数实现) |
4 | table.remove (table [, pos]) | 返回table数组部分位于pos位置的元素. 其后的元素会被前移. pos参数可选, 默认为table长度, 即从最后一个元素删起。 |
5 | table.sort (table [, comp]) | 对给定的table进行升序排序。 |
接下来我们来看下这几个方法的实例。
- Table 连接:我们可以使用 concat() 方法来连接两个 table:
fruits = {
"banana","orange","apple"}
-- 返回 table 连接后的字符串
print("连接后的字符串 ",table.concat(fruits))
-- 指定连接字符
print("连接后的字符串 ",table.concat(fruits,", "))
-- 指定索引来连接 table
print("连接后的字符串 ",table.concat(fruits,", ", 2,3))
执行以上代码输出结果为:
连接后的字符串 bananaorangeapple
连接后的字符串 banana, orange, apple
连接后的字符串 orange, apple
- 插入和移除:以下实例演示了 table 的插入和移除操作:
fruits = {
"banana","orange","apple"}
-- 在末尾插入
table.insert(fruits,"mango")
print("索引为 4 的元素为 ",fruits[4])
-- 在索引为 2 的键处插入
table.insert(fruits,2,"grapes")
print("索引为 2 的元素为 ",fruits[2])
print("最后一个元素为 ",fruits[5])
table.remove(fruits)
print("移除后最后一个元素为 ",fruits[5])
执行以上代码输出结果为:
索引为 4 的元素为 mango
索引为 2 的元素为 grapes
最后一个元素为 mango
移除后最后一个元素为 nil
- Table 排序:以下实例演示了 sort() 方法的使用,用于对 Table 进行排序:
fruits = {
"banana","orange","apple","grapes"}
print("排序前")
for k,v in ipairs(fruits) do
print(k,v)
end
table.sort(fruits)
print("排序后")
for k,v in ipairs(fruits) do
print(k,v)
end
执行以上代码输出结果为:
排序前
1 banana
2 orange
3 apple
4 grapes
排序后
1 apple
2 banana
3 grapes
4 orange
- Table 最大值:table.maxn 在 Lua5.2 之后该方法已经不存在了,我们定义了 table_maxn 方法来实现。
以下实例演示了如何获取 table 中的最大值:
function table_maxn(t)
local mn = 0
for k, v in pairs(t) do
if mn < k then
mn = k
end
end
return mn
end
tbl = {
[1] = "a", [2] = "b", [3] = "c", [26] = "z"}
print("tbl 长度 ", #tbl)
print("tbl 最大值 ", table_maxn(tbl))
执行以上代码输出结果为:
tbl 长度 3
tbl 最大值 26
13 模块与包
模块类似于一个封装库,从 Lua 5.1 开始,Lua 加入了标准的模块管理机制,可以把一些公用的代码放在一个文件里,以 API 接口的形式在其他地方调用,有利于代码的重用和降低代码耦合度。
Lua 的模块是由变量、函数等已知元素组成的 table,因此创建一个模块很简单,就是创建一个 table,然后把需要导出的常量、函数放入其中,最后返回这个 table 就行。以下为创建自定义模块 module.lua,文件代码格式如下:
-- 文件名为 module.lua
-- 定义一个名为 module 的模块
module = {}
-- 定义一个常量
module.constant = "这是一个常量"
-- 定义一个函数
function module.func1()
io.write("这是一个公有函数!\n")
end
local function func2()
print("这是一个私有函数!")
end
function module.func3()
func2()
end
return module
加载模块require
require("<模块名>")
或
require "<模块名>"
执行 require 后会返回一个由模块常量或函数组成的 table,并且还会定义一个包含该 table 的全局变量。
require("module")
print(module.constant)
module.func3()
14 元表
- setmetatable(table,metatable): 对指定table设置元表(metatable),如果元表(metatable)中存在__metatable键值,setmetatable会失败 。
- getmetatable(table): 返回对象的元表(metatable)。
以下实例演示了如何对指定的表设置元表:
mytable = {} -- 普通表
mymetatable = {} -- 元表
setmetatable(mytable,mymetatable) -- 把 mymetatable 设为 mytable 的元表
以上代码也可以直接写成一行:
mytable = setmetatable({},{})
以下为返回对象元表:
getmetatable(mytable) -- 这回返回mymetatable
一、__index 元方法
这是 metatable 最常用的键。
当你通过键来访问 table 的时候,如果这个键没有值,那么Lua就会寻找该table的metatable(假定有metatable)中的__index 键。如果__index包含一个表格,Lua会在表格中查找相应的键。
我们可以在使用 lua 命令进入交互模式查看:
$ lua
Lua 5.3.0 Copyright (C) 1994-2015 Lua.org, PUC-Rio
> other = {
foo = 3 }
> t = setmetatable({
}, {
__index = other })
> t.foo
3
> t.bar
nil
如果__index包含一个函数的话,Lua就会调用那个函数,table和键会作为参数传递给函数。
__index 元方法查看表中元素是否存在,如果不存在,返回结果为 nil;如果存在则由 __index 返回结果。
mytable = setmetatable({key1 = "value1"}, {
__index = function(mytable, key)
if key == "key2" then
return "metatablevalue"
else
return nil
end
end
})
print(mytable.key1,mytable.key2)
实例输出结果为:
value1 metatablevalue
实例解析:
- mytable 表赋值为 {key1 = “value1”}。
- mytable 设置了元表,元方法为 __index。
- 在mytable表中查找 key1,如果找到,返回该元素,找不到则继续。
- 在mytable表中查找 key2,如果找到,返回该元素,找不到则继续。
- -判断元表有没有__index方法,如果__index方法是一个函数,则调用该函数。
- 元方法中查看是否传入 “key2” 键的参数(mytable.key2已设置),如果传入 “key2” 参数返回 “metatablevalue”,否则返回 mytable 对应的键值。
我们可以将以上代码简单写成:
mytable = setmetatable({key1 = "value1"}, { __index = { key2 = "metatablevalue" } })
print(mytable.key1,mytable.key2)
二、__newindex 元方法
__newindex 元方法用来对表更新,__index则用来对表访问 。
当你给表的一个缺少的索引赋值,解释器就会查找__newindex 元方法:如果存在则调用这个函数而不进行赋值操作。
以下实例演示了 __newindex 元方法的应用:
mymetatable = {}
mytable = setmetatable({key1 = "value1"}, { __newindex = mymetatable })
print(mytable.key1)
mytable.newkey = "新值2"
print(mytable.newkey,mymetatable.newkey)
mytable.key1 = "新值1"
print(mytable.key1,mymetatable.newkey1)
以上实例执行输出结果为:
value1
nil 新值2
新值1 nil
以上实例中表设置了元方法 __newindex,在对新索引键(newkey)赋值时(mytable.newkey = “新值2”),会调用元方法,而不进行赋值。而如果对已存在的索引键(key1),则会进行赋值,而不调用元方法 __newindex。
以下实例使用了 rawset 函数来更新表:
mytable = setmetatable({key1 = "value1"}, {
__newindex = function(mytable, key, value)
rawset(mytable, key, "\""..value.."\"")
end
})
mytable.key1 = "new value"
mytable.key2 = 4
print(mytable.key1,mytable.key2)
以上实例执行输出结果为:
new value "4"
三、为表添加操作符
以下实例演示了两表相加操作:
-- 计算表中最大值,table.maxn在Lua5.2以上版本中已无法使用
-- 自定义计算表中最大值函数 table_maxn
function table_maxn(t)
local mn = 0
for k, v in pairs(t) do
if mn < k then
mn = k
end
end
return mn
end
-- 两表相加操作
mytable = setmetatable({ 1, 2, 3 }, {
__add = function(mytable, newtable)
for i = 1, table_maxn(newtable) do
table.insert(mytable, table_maxn(mytable)+1,newtable[i])
end
return mytable
end
})
secondtable = {
4,5,6}
mytable = mytable + secondtable
for k,v in ipairs(mytable) do
print(k,v)
end
以上实例执行输出结果为:
1 1
2 2
3 3
4 4
5 5
6 6
__add 键包含在元表中,并进行相加操作。 表中对应的操作列表如下:
模式 | 描述 |
---|---|
__add | 对应的运算符 ‘+’. |
__sub | 对应的运算符 ‘-’. |
__mul | 对应的运算符 ‘*’. |
__div | 对应的运算符 ‘/’. |
__mod | 对应的运算符 ‘%’. |
__unm | 对应的运算符 ‘-’. |
__concat | 对应的运算符 ‘…’. |
__eq | 对应的运算符 ‘==’. |
__lt | 对应的运算符 ‘<’. |
__le | 对应的运算符 ‘<=’. |
四、__call 元方法
__call 元方法在 Lua 调用一个值时调用。以下实例演示了计算表中元素的和:
-- 计算表中最大值,table.maxn在Lua5.2以上版本中已无法使用
-- 自定义计算表中最大值函数 table_maxn
function table_maxn(t)
local mn = 0
for k, v in pairs(t) do
if mn < k then
mn = k
end
end
return mn
end
-- 定义元方法__call
mytable = setmetatable({
10}, {
__call = function(mytable, newtable)
sum = 0
for i = 1, table_maxn(mytable) do
sum = sum + mytable[i]
end
for i = 1, table_maxn(newtable) do
sum = sum + newtable[i]
end
return sum
end
})
newtable = {
10,20,30}
print(mytable(newtable))
以上实例执行输出结果为:
70
五、__tostring 元方法
__tostring 元方法用于修改表的输出行为。以下实例我们自定义了表的输出内容:
mytable = setmetatable({ 10, 20, 30 }, {
__tostring = function(mytable)
sum = 0
for k, v in pairs(mytable) do
sum = sum + v
end
return "表所有元素的和为 " .. sum
end
})
print(mytable)
以上实例执行输出结果为:
表所有元素的和为 60
15 面向对象
Person = {age = 0}
function Person:new(o,a)
o = o or {}
setmetatable(o,self)
self.__index = self;
a = a or 0
self.age = a;
return o;
end
function Person:say()
print("age = ",self.age);
end
--创建对象
p1 = Person:new(nil,10)
--使用点号访问d对象的属性
p1.age =11;
--使用冒号来访问对象的成员函数
p1:say(); -- 或者使用p1.say(p1) 使用点访问时,系统不会自动传递当前的table给self
--继承
Student = Person:new()
function Student:new(o,side)
o = o or Person:new(o,side)
setmetatable(o,self)
self.__index = self;
return o;
end
function Student:say()
print("学生年龄 = ",self.age)
Person:say()--调用父类方法
end
s1 = Student:new(nil,10);
s1:say()
--输出
age = 11
学生年龄= 10
age = 10
16 lua与C++交互
Lua与C++交互
17 参考
W3CSchoolLua教程
今天的文章Lua教程_lua编程软件[通俗易懂]分享到此就结束了,感谢您的阅读。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/74564.html