Lua 脚本
一、Lua 简介
1.1 lua 解释器,luac 编译器
lua 参数: -i 进入交互模式
-e
执行单语句
-l
加载库文件
example:
lua -i -l math -e "print(math.sin(20))"
lua -i -e "_PROMPT='lua>'"
1.2. lua 解释器模式下,
执行文件 dofile("example.lua");
该模式下接受一个完整的程序块 chunk
1.3. 注释
1) 行注释 --这是注释
2) 块注释 --[[
这里被注释了
--]]
或
--[[
这里被注释了
]]
在 --[[ 多加一个减号。就变成了行注释
---[[
这里没有注释,会报错
--]]
1.4. 命令行参数table表,解释器在运行的时候会把所有的命令行参数作
为值,创建一个名称为arg的table。脚本名称位于索引0上,它的第一个
参数位于索引1上,脚本之前的所有参数位于负数索引上
1.5. = 输出
lua> = math.sin(3)
-->0.14112000805987
1.6. 环境变量
1)LUA_INIT
lua解释器在执行之前会先检查LUA_INIT环境变量,如果该环境变量存
在,则将解释器会先执行该 环境变量所标识的值。该值可以是一个lua语
句,也可以是一个以 @file.lua 的lua 文件
2) LUA_PATH
1.7. 全局变量
lua 中全局变量不需要声明,直接使用一个没有声明的变量。则认为该
变量为全局变量。全局变量若不用了,最后赋值为nil,全局变量的生命周期
为 ???
所有的全局变量都是存储在一个table表中
1.8. lua 动态类型语言。没有类型定义的语法。变量值"自带"了类型信息
1.9. 词法规范
1) 标识符,以任意字母、数字和下划线组成,且不能以数字开头。
注意:Lua 中的字母,由locale 区域设置决定。可以是拉丁字母或者
其它语系的字母
2)保留标识符,lua 中通常将以一个下划线开头并后跟大写字母的标识
符作为哑变量。 例如_VERSION _PROMPT
3)Lua 中的保留关键字。
and
break
do
else
elseif
end
false
for
function
if
in
local
nil
not
or
repeat
return
then
true
until
while
4)Lua 区分大小写
1.10 Lua 的所有标准库都是用C写的
1.11 misc
tonumber() 只能处理 纯数字的字符串
tonumber("123c")
nil
tonumber("-123")
-123
tonumber("+123")
123
二、类型与值
2.1. lua 中的变量类型
bool number string nil userdata(自定义类型) function table
thread
type(1) 输出类型的函数。--> number
该 函数输出的永远是字符串。
print(type(type(x))) 无论x 为任何类型的值,输出都为string
2.2. nil 类型只有一个值 nil
只有false 和 nil 才为false, 其它都为 ture.注意:0和空字符串都
为ture
2.3. lua 中的数值类型 number。
在lua 中没有整型,整型和浮点型都用number.number 可以表示任何
32位整数。
b = 3.1415 <===> 0.31415e1
可以通过修改luaconfig.c文件中定义来修改用long 或 float 来表示
一个number ???
2.4. string类型
1)lua 中的字符串是不能修改的,如果要修改一个字符串,则需要重新
创建一新的字符串。lua 自动管理字串串内存的释放
字面字符串, 使用 单引号,或双引号括起来的字符串。
'hello world' "do go go"
string.gsub("one string", "one", "another")
注意:lua 能高效的处理长字符串
2).. 字符串连接符
print(10..20)
a = 10
b = "hello"
c = a .. b
print(c)
10hello
TODO:字符串的各种使用
???
3)字符
\ 十进制 一次读3个数字,或者遇到非数字终止
转义字符,
同C语言中的差不多。
str = "hello\nworld!"
2.5 table 表
Lua 中唯一的数据结构机制
1.特点:
a.无大小限制
b.关联式数组
c.key 值可以是number 也可以是 string,并且不需要key值为同
一类型。
d.使用之前必须构造。 注意:table表变量不同于number和string
可以直接使用,table必须使用构造表达式{} 构造之后才可以使用。
e:只有table的变量和 该变量的引用没有固定的关联性。也就是说
表单a 作为值,给b赋值赋值之后,a=nil b 也不会为空。可以将table
表单理解为在动态内存中分配的一个空间,
a = {}
a[3] = 4
a["3"] = 3
b = a
a = nil
--表单b依然是原来的值
2. 当table 没有任何引用时,Lua垃圾回收器会自动回收table所占
内存。当table的 key 值为 字符串时,可以使用 Lua 提供了语法糖简
便的来表示。例如 a["name"] <==> a.name
3. #长度操作符
#长度操作符,返回数组或线性表最大索引值。
长度操作符是根据 nil 来界定table大小的。因此当table中
间有nil值时,通过#操作符获得的长度将是错误的。
4. table 最多正数 key值
table.maxn(a) 可以获得 table的最多正索引数
2.6 userdaata结构
自定义类型,它可以将任意类型的C语言数据都存储到Lua 变量中。
它用于表示,一种由应用程序或C语言库所创建的新类型。
三、操作符和表达式
3.1. 算数操作符
二元:
+ - * /
% : 和C 语言中的取模不同,这里支持实数
【****】
例如 3.141592653 % 0.01 == 3.14
^(指数) 3^0.5
右结合
【*】
一元:
-
3.2. 逻辑表达式
二元:
and
或
or
与
一元
not
非
example:
1) not 运算符永远返回ture 或 false
print(not nil)
-->ture
print(not false)
-->ture
2) and or 表达式返回的结果: 终止该表达式的那个操作数结果
a and b
--> 如果 a 为 真 b 为 假, 返回 b, a 假 返回 a ;a 真 b假 返回 b
a or b
-->同上
3) a and b or c 类型 a ? b:c 前提 b 必须为真,否则 a =ture
b = false 则返回 c
3.3. 关系表达式
<
小于
>
大于
<=
小于等于
>=
大于等于
~=
不等于
1)关系运算符永远只返回true false
2)只有 number 和 string 才有 相等测试以外的运算
3)不同类型的值,不知允许做相等性测试
4) nil 等于 nil
5) table、userdata 和函数,只比较引用,并且不能做非相等性比较
6) string 类型的值在比较傲时,采用字母次序进行比较。这依赖于
解释器的区域设置。
example:
2 < "15"
非法表达式
"2" < "15"
false
a = {}
a.x = 1 a.y = 0
b = {}
b.x = 1 b.y = 0
c = a
a == b
false
只做引用比较
b == c
false
只做引用比较
a == c
ture
只做引用比较
3.4 运算符的优先级问题
^ > 一元操作符 > 算数操作符 > .. > 关系操作符 > and > or
.. 和 ^ 右结合
其他的都是 左结合
3.5 table 构造式
1. 空构造式
a = {}
2. 记录风格构造式 下标从 1开始
a = {"Sunday", "Monday", "Thuesday", "Wednesday",
"Thursday", "Friday", "Saturday"}
3. 列表风格式构造式
a = { x = 3, y = 4, z = 5}
字段名称为标识符,也就是字符串
4. 二者混合风格
a = {
x = 3, y = 4, z = 5,
{color="blue", px=1},
-- key == 1
{color="red", px=1},
-- key == 2
{color="green", px=1}
-- key == 3
}
这种情况下,没有指定key , 从第一没有指定Key值得value开始
key 从 1 开始累加,如果指定的key 值与默认的重复,丢弃指定的
以上三种构造式,key 值都不允许使用 算数运算符,也不允许
出现负数。运行符(+ - * / 等等)也不能用
a = {}
5. 包含运算的构造式 []
s = 3
a = {["+"] = "add", 1, 2, [s] = 3}
注意:当默认的table下标 同 构造式中指定的下标冲突时,解释
器只认默认的下标。如过在构造式中,指定了两个相同的key 值,则第
二个key值所代表的值,会覆盖掉第一个值
example:
a = {1, 2, 3, [1] = 10}
print(a[1])
--> 1 注意不是 10
a = {x= 3, y = 4, x = 5}
print(a.x)
--> 5 注意和上面的区别
--综合应用
example:
local str = ""
local i = 10
b = { 1, 2, 3,
[i] = 10,
4, 5, 6,
[i] = 50,
[i-3] = 7,
[i-2] = 8,
[i-1] = 9
}
for i = 1, 10 do
str = str .. " " .. b[i]
end
print(str)
--> 1 2 3 4 5 6 7 8 9 50
3.6 位运算
???
四、语句和控制结构
4.1 赋值语句
1. 单个变量赋值
a = 12
str = "Hello" .. " " .. "World"
2. 多重赋值
【***】
将多个值,赋给多个变量; 解释器先对等号右边的表达式求值,然后
在依次赋值
多重赋值的执行效率并不比 普通的单个变量赋值要快。多重赋值主要
用在函数返回多个值时。
在赋值的时候,解释器总是会让右边的值个数等于左边变量的个数。当
值多余变量个数时,多余的值会被丢弃;当值得个数少于变量个数时,缺少
的值,会用nil替代
a = 1 b = 2
a, b = b, a
-- 这样可以交换两个变量的值
a, b, c = 1, 2, 3
a, b, c = 1, 2
--> c = nil
4.2 分支语句
if end
if else end
if elseif else end
4.3 循环语句
循环语句 终止语句 break。
需要特别注意的是,在循环的时候是不要修改 控制变量的值,这样会
导致不可以预测的结果。 【***】
1. for
for 循环有两种形式,一种是 数字型for ; 一种是 泛型for
1) 数字型for
for var = initValue, endValue, step do
...
end
说明:
a) step 可以省略,默认步长为 1;
b) 起始值和终止值,都是闭合的 [initValue, endValue]
c) 如果不希望设置一个上限,可用 math.huge 常量,这样
会导致无限循环
d) initValue, endValue, step 可以是表达式,但是该表达
式只会计算一次。
example:
local i = 2
--不声明就会使用一个全局的 i
for i = 1,
-- 最后执行
i * 100,
-- 先计算 这部分
i * 2
-- 再计算 这部分
do
print(i)
end
等价于
for i = 1, 200, 4 do
print(i)
end
2) 泛型for
泛型for 是通过迭代器来实现的,Lua 基础库中提供了几种常用的
迭代器。
a) ipairs 迭代数组
???
for i, v in ipairs(a) do
print(v)
end
b) pairs 迭代table
for k in pairs(t) do
print(k)
-- 打印t 中所有的Key值
end
for k, v in pairs(t) do
print(k , v)
-- 打印t 中所有的元素
end
ipairs 和 pairs 的区别:
???
c) io.lines 迭代文件每一行
d)string.gmatch 迭代字符串中单词
2. while
while condition do
...
end
3. repeat
repeat
...
until condition
这种结构类似于C 中的 do ..while()
4.4 变量作用域和 程序块
1. 局部变量
使用local声明的变量,为局部变量
作用域:声明的程序块、函数体、控制结构的执行体
注意:Lua 中在同一个程序块中允许相同名称而作用域不同的变量,
应尽量使用局部变量;多余的全局变量会使程序混乱;并且程序也
会浪费内存空间;还有访问全局变量的速度要比访问局部变量的要
慢。
local foo = foo
-->将全局变量的foo值赋给局部变量foo
这样做的目的,当前全局foo改变时,不影响当前程序快;还有访问
速度要快
2. 全局变量
全局变量被赋nil 值后,消失
3. do end
do end 可以显示的表示一个程序块
4.5 break 、return
【***】
1)这两个语句,不同于 中的 break 和 return 。由于Lua 语法构造的
原因,bread/return 都必须处在结束(break为循环体的结束位置、return
为函数的结束,或者 break的结束位置)的位置,后面不允许有其他语句。
example:
function foo()
while ture do
break
print("Hello World!")
-- 不合法
end
return
print("return")
-- 不合法
end
2)没有函数的结束处都有默认的return 语句,所以在没有返回值的
情况下不需要t添加return语句
3) do return / break end 可以解除 第一条的限制
example:
function foo()
while ture do
do
break
end
print("Hello World!")
-- 合法
end
do
return
end
print("return")
-- 合法
end
五、函数和模块
5.1.函数
1. 函数定义
-- 全局函数声明
function funcName(argList...)
... -- 完成特定工作的一系列语句
end
-- 局部函数声明, 并且只能在定义的时候声明
local function funcName(argList...)
... -- 完成特定工作的一系列语句
end
说明:声明为局部的函数,只能在声明时的那个块内调用
2. 函数的调用
func()
当函数值只有一个参数,且这个参数是字面字符串,或者table构造
式时,可以不使用圆括号。
-- 参数用两个中括号括起来的,[[字面字符串,可以写成多行]]
print [[ a mutil-line
message]]
print "hello world"
print ("hello world")
-- table 构造式做参数的时候可以省略 圆括号
func{x=1, y=2}
3. 参数
1)形参列表
2) 实参列表
在函数调用的时候,允许实参列表的参数个数同形参列表的个数不同,
解释器会自动调整个数。规则:多了丢弃、少了nil替代
3) 具名实参
使用具名实参的必要条件:
a) 只有一个参数
b) 使用Lua 特殊的函数调用语法 rename{} 大括号
原理:具名实参,实际上是利用table的特性来实现的。table的
列表风格式构造式,可以指定table中字段的名称。
example:
-- 矩形
function rect(arg)
return arg.x, arg.y, arg.width, arg.height
end
a = {rect{x=1, y = 2, width= 10, height = 10}}
for i, v in ipairs(a) do
print(i, v)
end
4) 变长参数 ...
a) 变长参数的使用
...
b) 变长参数值得获取
使用时直接使用 ... 3个点就可以了。类似于
aa)不包含 nil 变长参数的遍历
for i, v in ipairs{...} do
end
这种遍历方法,当传入的值中包含了 nil 值时,遍历
的结果会达不到预期要求。这时需要 select
bb) 包含nil值得遍历
当变长参数中包含了空值时,可以使用select 函数来
获得该数组的最多key 值,然后挨个遍历
do
local count = select("#", ...)
for i = 1, count do
if not a[i] then
--注意判断nil
print(a[i])
end
end
end
说明:select 函数的用法
select 第一个参数是selector,必须提供。并且
selector 只能是 数组下标,或者字符串"#":
当"#"时,返回字符数组最大下标;
当 n 时,返回下标为n 以及n之后的参数值
c) 作为参数返回
return ...
d) 赋值
local a, b, c = ...
4. 返回值
1) 多重返回值
Lua 具有多重返回值。
注意:只有当函数的调用处在赋值语句的最末尾时,
才会有多个返回值
--假设 foo() 返回两个值
a, b = foo()
-- a, b 为返回的两个值
a, b = 1, foo()
-- 丢弃了一个值
a, b = foo(), 1
-- 只接受了一个值
2) 强制要求只返回一个返回值
一对圆括号可以强制使,返回的多个值,只返回一个作为其他表
达式的参数。
print(foo())
-->1 2
print( (foo()) )
-->1
3) table 可以接受所有的返回值
a = {foo()}
a = {foo(), 1}
--但这种情况也只接受一个返回值,原理
同第一个,除非在末尾调用
5. 参数返回相关
【***】
1) 使多重返回值,只返回第一个值
(foo()) 小括号
2) 接受多个返回值,并返回多个值 unpack{foo()}
unpack(table) 接受一个table 并返回table 中的所有值