在此前,仅仅是了解tolua的客户端实现热更新的一个框架代码,对于lua由于用的较少,日子久了反而忘记的很快,在此记录下笔记,对于lua的基本语法总结于此。
lua的运行环境
安装LuaForWindows_v5.1.4-39
lua的文件格式:xxx.lua
注:文件不可直接运行,也不需要像c++那般进行编译,运行环境内部存在一个luac.exe的程序(执行lua代码);程序没有入口函数,在文件中从上往下依次执行
Lua特点
可扩展性很强,在游戏领域lua主要用于做界面的开发,可与c++/c#这些大型语言进行结合;
简单,小巧,无法用于开发大型的功能,基于一门大型主语言进行开发;
高效率;
与平台无关。
概念
Chunks
chunk是lua执行的一个语句,也可以是每一块语句(即一系列语句的组合),还可以是函数。
注:lua当中每个语句结尾的分号(;)是可选的,但是如果在同一行存在多个语句最好是用;分开
全局变量
lua当中全局变量不需要声明(局部变量也是如此),给一个变量赋值后即创建了这个全局变量,访问一个没有初始化的全局变量野不会出错,只不过得到的结果是:nil。
g_value =10
print(g_alue) --假若变量敲错了 lua并不会报错
print(g_value)
g_value = nil --如果你想删除一个全局变量,只需要将变量负值为nil
--即当且仅当一个变量不等于nil时,这个变量存在
print(g_value)
--输出结果为:
--nil
--10
--nil
注:lua是对大小写敏感;
注释符
单行注释符:--
多行注释符:--[[ --]]
--[[
vas = 20; --定义一个全局变量
print(g_vas);
--]]
基本数据类型
lua当中变量不需要类型定义;存在8个基本类型:nil、boolean、number、string、userdata、function、thread、table。
nil:Lua中特殊的类型,他只有一个值:nil;一个全局变量没有被赋值以前默认值为nil;给全局变量负nil可以删除该变量。
boolean:存在两个取值false和true。但要注意Lua中所有的值都可以作为条件。在控制结构的条件中除了false和nil为假,其他值都为真。所以Lua认为0和空串都是真。
number:表示实数,Lua中没有整数。Lua的numbers可以处理任何长整数不用担心误差。
string:指字符的序列。lua是8位字节,所以字符串可以包含任何数值字符,Lua中字符串是不可以修改的,并且string和其他对象一样,Lua自动进行内存分配和释放,可以使用单引号或者双引号表示字符串
function:表示函数,可以存储在变量中,可以作为函数的参数,也可以作为函数的返回值
函数type:测试给定变量或者值的类型
print:打印输出的函数
print(type("Hello world")) --> string
print(type(10.4*3)) --> number
print(type(print)) --> function
print(type(type)) --> function
print(type(true)) --> boolean
print(type(nil)) --> nil
print(type(type(X))) --> string
算术运算符
二元运算符:+ - * / ^ (加减乘除幂)
一元运算符:- (负值)
注:这些运算符的操作数都是实数。
print(1+2) -- 3
print(1-2) -- -1
print(1*2) -- 2
print(1/2) -- 0.5
print(2^2) -- 4
关系运算符
存在以下几种运算符:< > <= >= == ~=
注:
- 操作符返回结果为false或者true;==和~=比较两个值,如果两个值类型不同,Lua认为两者不同;nil只和自己相等;
- Lua通过引用比较tables、userdata、functions。也就是说当且仅当两者表示同一个对象时相等;
- Lua比较数字按传统的数字大小进行,比较字符串按字母的顺序进行,类似于strcmp;
- 混合比较数字和字符串,Lua会报错,比如:2 < "15"。
print(1>2) --false
print(1>=2) --false
print(1<2) --true
print(1<=2) --true
print(1==2) --false
print(1~=2) --true
print(1 == type(1)) --false
逻辑运算符
存在以下几种运算符:and or not
注:
- 逻辑运算符认为false和nil是假(false),其他为真,0也是true.
- and和or的运算结果不是true和false,而是和它的两个操作数相关。
print(1<2 and 1>2) --false
print(1<2 or 1>2) --true
print(not(1<2)) --false
print(not nil) --true
print(not 0) --false
a=4
b=5
print(a and b) -- 5 如果a为false,则返回a,否则返回b
print(a or b) -- 4
print(nil and 13) -- nil
print(false and 13) -- false
print(false or b) -- 5 如果a为true,则返回a,否则返回b
-- 一个很实用的技巧:如果x为false或者nil则给x赋初始值v
x = x or a
--等价于
if not x then
x = a
end
--打印a的值
print(x); --4
连接运算符
符号:..
注:字符串连接,如果操作数为数字,Lua将数字转成字符串。
print("Hello " .. "World") --> Hello World
value = 100
print(value .. 'rmb') --> 100rmb
优先级
从高到低的顺序:
^
not - (unary)
* /
+ -
..
< > <= >= ~= ==
and
or
注:除了^和..外所有的二元运算符都是左连接的。
表(table)
表是Lua特有的功能强大的东西。最简单的构造函数是{},用来创建一个空表;构造函数可以使用任何表达式初始化;不管用何种方式创建table,我们都可以向表中添加或者删除任何类型的域,构造函数仅仅影响表的初始化。
两次形式:
数组型:
注:
- 第一个元素索引为1,即下表从1开始;
- 越界返回nil,程序不会崩溃;
days = {"Sunday", "Monday", "Tuesday", "Wednesday","Thursday", "Friday", "Saturday"}
print(days[4]) -- Wednesday
print(days[9]) -- nil
record型:
a = {x=3, y=4}
--可写成
--[[
a =
{
x=3,
y=4
}
--]]
--等价于 a = {}; a.x=0; a.y=0
print(a.x) --3 类似于结构体的方式访问元素
print(a.y) --4
print(a['x']) --3 --类似于map键值对的方式访问元素
--向表中添加或者删除任何类型的域
w = {x=0, y=0, label="console"}
x = {1, 2, 3}
w[1] = "hello word"
x.f = w --向x表中添加成员f,f即是表w
print(w["x"]) --> 0
print(w[1]) --> hello word
print(x.f[1]) --> hello word
w.x = nil -- remove "x"
在同一个构造函数中可以混合列表风格和record风格进行初始化:
polyline = {color="blue", thickness=2, npoints=4,
{x=0, y=0},
{x=-10, y=0},
{x=-10, y=1},
{x=0, y=1}
}
print(polyline[1].x) -- 0
print(polyline[2].x) -- -10
opnames = {["+"] = "add", ["-"] = "sub",["*"] = "mul", ["/"] = "div"}
i = 20; s = "-"
a = {[i+0] = s, [i+1] = s..s, [i+2] = s..s..s}
print(opnames[s]) --> sub
print(a[22]) --> ---
实现数组型表的下表从0开始(不推荐数组下标从0开始,否则很多标准库不能使用)
days = {[0]="Sunday", "Monday", "Tuesday", "Wednesday","Thursday", "Friday", "Saturday"}
print(days[0]) --> Sunday
在构造函数的最后的","是可选的,可以方便以后的扩展;在构造函数中域分隔符逗号(",")可以用分号(";")替代,通常我们使用分号用来分割不同类型的表元素。
t = {a=10, b=45; "one", "two", "three",}
赋值语句
赋值是改变一个变量的值和改变表域的最基本的方法,Lua可以对多个变量同时赋值,变量列表和值列表的各个元素用逗号分开,赋值语句右边的值会依次赋给左边的变量。
注:多值赋值经常用来交换变量,或将函数调用返回给变量。
value1, value2 = 10, 2*10 -- value1=10; value2=20
print('value1 = ' .. value1);
print('value2 = ' .. value2);
--遇到赋值语句Lua会先计算右边所有的值然后再执行赋值操作
--所以我们可以这样进行交换变量的值:
value1,value2 = value2,value1
print('value1 = ' .. value1);
print('value2 = ' .. value2);
当变量个数和值的个数不一致时,Lua会一直以变量个数为基础采取以下策略:
- 变量个数 > 值的个数,则按变量个数补足nil
- 变量个数 < 值的个数,则多余的值会被忽略
a, b, c = 0, 1
print(a,b,c) --> 0 1 nil
局部变量与代码块
使用local创建一个局部变量,与全局变量不同,局部变量只在被声明的那个代码块内有效。代码块:指一个控制结构内(if-else等等),一个函数体,或者一个chunk(变量被声明的那个文件或者文本串)。
value = 1
if true then
local value = 2;
print('local:value = ' .. value) --2
end
print('grobal:value = ' .. value) --1
控制结构语句
涵盖if-else语句、while语句、repeat-until语句、for循环
注:控制结构的条件表达式结果可以是任何值,Lua认为false和nil为假,其他值为真。
if-else语句:
格式:
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 condition do
statements;
end;
repeat-until语句:
格式:
repeat
statements;
until conditions;
示例代码:
--if-else
state = 5
if state < 4 then
print('state:'.. state .. '<4')
elseif state>=4 and state<10 then
print('state:'.. state .. '>=4 and state <10')
else
print('i>=10')
end
---while
local i =0
while i < 10 do
i=i+1
print(i)
end
--repeat-until
local j = 0
repeat
j = j+1
print(j)
until j >= 10
for循环:
数值for循环:
for将用exp3作为step(步长)从exp1(初始值)到exp2(终止值),执行loop-part。其中exp3可以省略,默认step=1
注:
- 三个表达式只会被计算一次,并且是在循环开始前;
- 控制变量var是局部变量自动被声明,并且只在循环内有效;
- 如果需要保留控制变量的值,需要在循环中将其保存;
- 循环过程中不要改变控制变量的值,那样做的结果是不可预知的。如果要退出循环,使用break语句。
- Lua当中不存在continue语句,存在break和return
格式:
for var=exp1,exp2,exp3 do
loop-part
end
代码示例:
for i =1,10,2 do
print(i) --13579
end
for i =1,10 do
print(i) --123456789
end
范型for循环:
--如果遍历数组型表,key为整型则ipairs和pairs都可使用,否则只能用pairs
t1= {10,20,30,40,50,60}
for key,value in ipairs(t1) do
print(key..','..value) --1,10 2,20 3,30 4,40 5,50 6,60
end
for key,value in pairs(t1) do
print(key..','..value) --1,10 2,20 3,30 4,40 5,50 6,60
end
t2= {x=11,y=21,z=31}
for key,value in pairs(t2) do
print(key..','..value) --y,21 x,11 z,31
end
函数
函数有两种用途:
- 完成指定的任务,这种情况下函数作为调用语句使用;
- 计算并返回值,这种情况下函数作为赋值语句的表达式使用。
格式:
function func_name (arguments-list)
statements-list;
end;
示例代码:
function funtmp1(a,b)
if a > b then
print('a > b')
return a-b
else
print('a < b')
return b-a
end
end
local res = funtmp1(100,200)
print(res) --a<b 100
function funtmp2(a,b)
return a+b,a-b,a*b,a/b
end
--多值赋值经常用来交换变量,或将函数调用返回给变量
local ret1,ret2,ret3 = funtmp2(100,200)
print(ret1..','..ret2..','..ret3) --300,-100,20000
local ret11,ret22,ret33,ret44 = funtmp2(100,200)
print(ret11..','..ret22..','..ret33..','..ret44) --300,-100,20000,0.5
Lua的面向对象
Lua中的表不仅在某种意义上是一种对象;两个不同值的对象(table)代表两个不同的对象;对象有他们的成员函数。
Account = {balance = 0}
--[[方法1
function Account.withdraw (v)
Account.balance = Account.balance - v
print(Account.balance) -- -100
end
Account.withdraw(100.00)
a = Account; Account = nil
a.withdraw(100.00) -- ERROR!
--]]
--违背了前面的对象应该有独立的生命周期的原则
--定义方法的时候带上一个额外的参数,来表示方法作用的对象。这个参数经常为self或者this
--[[方法2
function Account.withdraw (self, v)
self.balance = self.balance - v
print(self.balance) -- -100
end
a1 = Account; Account = nil
print(a1.balance) --0
a1.withdraw(a1, 100.00) -- OK
--]]
--通过使用冒号操作符来隐藏这个参数的声明。我们可以重写上面的代码
--[[方法3
function Account:withdraw (v)
self.balance = self.balance - v
print(self.balance) -- -100
end
a = Account; Account = nil
a:withdraw(100.00)
--]]
--两种用法的使用总结
Account = {
balance=0,
withdraw = function (self, v)
self.balance = self.balance - v
print(self.balance) --200
end
}
function Account:deposit (v)
self.balance = self.balance + v
print(self.balance) --100
end
Account.deposit(Account, 200.00)
Account:withdraw(100.00)
参考文献:lua程序设计