数学库
三角函数(sin,cos,tan……)
所有的三角函数都使用弧度单位,可以用函数deg(角度)和rad(弧度)来转换角度和弧度。示例代码:
print(math.sin(math.rad(30))) -- 0.5
谨记:三角函数的参数都是弧度,在实际使用中不要忘了,是弧度。
取整函数(floor,ceil)
floor:返回不大于x的最大整数;向下取整;
ceil:返回不小于x的最大整数;向上取整。示例代码:
print(math.floor(5.6)) -- 5
print(math.ceil(5.6)) -- 6
最大值和最小值(max,min)
max:取参数中的最大值;
min:取参数中的最小值。示例代码:
print(math.max(2, 3, 2, 14, 2, 30, -3)) -- 30
print(math.min(2, 3, 2, 14, 2, 30, -3)) -- -3
生成伪随机数的函数(random,randomseed)
在实际开发中,生成随机数的需求是经常有的。使用random和randomseed这两个函数就可以轻易的完成。math.random用于生成伪随机数,可以用3种方式来调用它:
(1)如果在调用时不提供任何参数,它将返回一个在区间[0, 1)内均匀分布的伪随机实数;
(2)如果提供了一个整数n作为参数,它将返回一个在区间[1, n]内的伪随机整数;
(3)如果提供了两个整数参数m和n,它将返回一个在区间[m, n]内的伪随机整数。
示例代码如下:
函数math.randomseed用于设置伪随机数生成器的种子数。
一般我们的做法是在一个程序启动时,用一个固定的种子数来调用它,以此初始化伪随机数生成器。
那么如何设置这个math.randomseed的种子值呢?如果使用同一个种子值的话,每次得到的随机数就会是一样的,在实际开发中,一般都是使用当前时间作为种子值,比如:
math.randomseed(os.time())
这样就好了。一般在我们的程序启动时,初始化一次种子就足够了。我曾经傻傻的在一个循环中,使用math.random取随机数,每次都调用math.randomseed(os.time())设置种子值
math.randomseed(os.time())
print(math.random()) -- 输出一个大于等于0,小于1的值
print(math.random(2)) -- 输出不是1就是2
print(math.random(3, 4)) -- 输出不是3就是4
table库
table库是由一些辅助函数构成的,这些函数将table作为数组来操作(重点:作为数组来操作的)。
插入和删除函数
table.insert用于将一个元素插入到一个数组的指定位置,它会移动后续元素以空出空间。如果在调用table.insert时没有指定位置参数,则会将元素添加到数组末尾。示例代码:
local tb = {10, 20, 30}
table.insert(tb, 40) -- 在table的最后插入,结果为:{10, 20, 30, 40}
table.insert(tb, 2, 15) -- 在table的位置2插入,结果为:{10, 15, 20, 30, 40}
排序
local tb = {20, 10, 2, 3, 4, 89, 20, 33, 2, 3}
-- 默认是升序排序
table.sort(tb)
for _, v in ipairs(tb) do
print(v)
end
print("=======")
-- 修改为降序排序
table.sort(tb, function (a, b) if a > b then return true end end)
for _, v in ipairs(tb) do
print(v)
end
但是,在实际开发中,我们经常犯这样的错误,总是试图对一个table的索引进行排序。在table中,索引是一个无序的集合。
如果对它们进行排序,则必须将它们复制到一个数组中,然后对这个数组进行排序。这就是我为什么一开始强调的,table库是对数组进行操作的。
local tb = {x = 20, z = 10, y = 30, m = 2, n = 8} -- 这是一个key无序的table
local keyTb = {}
for k, _ in pairs(tb) do
keyTb[#keyTb + 1] = k
end
table.sort(keyTb)
for _, v in ipairs(keyTb) do
print(v .. " = " .. tb[v])
end
连接
使用table.concat可以完成数组的连接。它接受一个字符串数组,并返回这些字符串连接后的结果,它有一个可选参数,
用于指定插到字符串之间的分隔符,同时这个函数另外还接受两个可选参数,用于指定第一个和最后一个要连接的字符串索引。示例代码:
local tb = {"Jelly", "Think", "Is", "Good"}
local strTb = table.concat(tb, " ")
print(strTb)
完整I/O模型
简单I/O功能太受限了,以至于基本没有什么用处,而用的更多的则是这里说的完整I/O模型。完整I/O模型可以进行更多的I/O控制,它是基于文件句柄的,就好比与C语言中的FILE*,表示一个正在操作的文件。
要打开一个文件,可以使用io.open函数,它有两个参数,一个表示要打开的文件名,另一个表示操作的模式字符串。模式字符串可以有以下四种取值方式:
(1)”r”:以读取方式打开文件;只能对文件进行读取操作;
(2)”w”:以写入方式打开文件;可以对文件进行写入操作,但是会覆盖文件原有内容;
(3)”a”:以追加方式打开文件;可以对文件进行写入操作;会在原来文件的基础在,进行追加写入;
(4)”b”:表示打开二进制文件,这种模式一般都是和前面三种混合使用,比如:”rb”,”wb”。
open函数会返回表示文件的一个句柄;如果发生错误,就返回nil,一条错误消息和一个错误代码。示例代码:
-- 访问一个不存在的文件
print(io.open("ooxx.txt", r))
--[[
输出以下内容:
nil ooxx.txt: No such file or directory 2
--]]
当成功打开一个文件以后,就可以使用read/write方法读写文件了,这与read/write函数相似,但是需要用冒号语法,将它们作为文件句柄的方法来调用,示例代码:
local hFile = io.open("input.txt", r)
if hFile then
local strContent = hFile:read("*all")
--local strContent = hFile.read(hFile, "*all") 你也可以使用这种方法
print(strContent)
end
time
如果不带任何参数调用time函数,它会以数字形式返回当前的日期和时间。
print(os.time()) -- 输出当前时间的描述
date
函数date是time的一个反函数,它可以将一个表示日期和时间的数字转换成某些高级的表现形式。
其第一个参数是格式字符串,指定了期望的表示形式;第二个参数是日期和时间的数字,默认为当前日期和时间。例如:
local tbCurrentTime = os.date("*t")
for k, v in pairs(tbCurrentTime) do
print(k .. "=" .. tostring(v))
end
assert
当Lua遇到不期望的情况时就会抛出错误,比如:两个非数字进行相加;调用一个非函数的变量;访问表中不存在的值等。你也可以通过调用error函数显示的抛出错误,error的参数是要抛出的错误信息。
assert(a,b) a是要检查是否有错误的一个参数,b是a错误时抛出的信息。第二个参数b是可选的。
assert首先检查的是第一个参数是否返回错误,如果不返回错误,则assert简单返回,否则则以第二个参数抛出异常信息。
assert()是普通函数,他首先计算两个参数,然后在调用函数,如:
n = io.read()
assert(tonumber(n), "invalid input:" .. n .. "is not a number")
先进行tonumber(n), "invalid input:" .. n .. "is not a number"这两个参数的计算。
没用assert( )时:
input = io.read("*number")
print(input)
运行结果:
nil
[Finished in 0.3s]
用assert( )时:
input = assert(io.read("*number"))
print(input)
运行结果:
lua: D:\UserProfiles\BenLuo\Desktop\study\test.lua:44: assertion failed!
stack traceback:
[C]: in function 'assert'
D:\UserProfiles\BenLuo\Desktop\study\test.lua:44: in main chunk
[C]: ?
[Finished in 0.3s with exit code 1]
local name = io.read();
local result = assert(name == "笨木头", "你是一个非常善良有爱心的人,我很喜欢你..所以,去死吧!");
如果assert的第一个参数为不为false,则返回第一个参数的值;否则,执行error函数,输出错误信息,错误信息的内容为assert的第二个参数。
error
当我们在调用一个函数之前,可以先判断即将传递的参数是否正常,如果不正常,我们就可以选择直接抛出error,方便写代码的过程中发现问题。
local name = io.read();
if name ~= "笨木头" then
error("你是一个非常善良有爱心的人,我很喜欢你..所以,去死吧!");
end
只要发现输入的内容不是“笨木头”,就狠狠地抛出异常。
pcall
如果在错误发生时,我们不希望代码停止运行,而是做一些紧急措施,那么,可以使用pcall捕获错误。
function test()
print(a[1]);
end
if pcall(test) then
print("正常,呵呵");
else
print("哎,函数出错了,我正在帮你处理,放心吧,等我睡醒就...不是,等你睡醒就没事了~");
end
函数test执行的时候肯定会报错的,因为并不存在a这个table。
使用pcall调用test函数,如果test不报错,则pcall返回ture,否则,返回false。
利用这个特性,我们就可以捕获异常,做一些紧急处理。
pcall除了会返回true或者false外,还能返回函数的错误信息,如下代码:
function test()
print(a[1]);
end
local status, err = pcall(test);
if status then
print("正常,呵呵");
else
print("哎,函数出错了,我正在帮你处理,放心吧,等我睡醒就...不是,等你睡醒就没事了~");
print(err);
end
pcall的第一个返回值和之前一样,true或者false。
而第二个参数则是test函数抛出的错误信息,执行代码,结果如下:
[LUA-print] 哎,函数出错了,我正在帮你处理,放心吧,等我睡醒就…不是,等你睡醒就没事了~
[LUA-print] [string "src/main.lua"]:94: attempt to index global ‘a' (a nil value)
数学函数
这些表函数同样可以参考Lua函数库(他们都是以math.开头, 详情参阅数学库了解更多信息).
abs(value) - 返回当前数值的绝对值
acos(value) - 返回此角度值的弧余弦值.
asin(value) - 返回此角度值的弧正弦值
atan(value) - 返回此角度值的弧正切值
atan2(y, x) - 在角度中, 返回Y/X的弧正切值.
ceil(value) - 返回一个整数, 不管小数点后面是多少, 整数部分都进1
cos(degrees) - 返回角度的余弦值
deg(radians) - 弧度转换角度
exp(value) - 返回此值的指数值
floor(value) - 返回此值的整数值
frexp(num) - 返回当前数字小数点后的数值以及小数点后的位数
ldexp(value, 倍数) - 输出此数值的倍数
log(value) - 返回此值的自然对数 (以e为底数)
log10(value) - 返回以10为底数的值
max(value[, values...]) - 在一堆值中查找最大值
min(value[,values...]) - 在一堆值中查找最小值
mod(value,modulus) - 返回此值的余数
rad(degrees) - 角度转换弧度
random([ [lower,] upper]) - 返回一个随机数字 (可选界限为整数值)
randomseed(seed) - 为伪随机数字生成器设定种子
sin(degrees) - 返回当前角度的正弦值
sqrt(value) - 返回数值的平方根值(比如100的平方根为10)
tan(degrees) - 返回当前角度的正切值
table.concat(table, sep, start, end)
concat是concatenate(连锁, 连接)的缩写. table.concat()函数列出参数中指定table的数组部分从start位置到end位置的所有元素, 元素间以指定的分隔符(sep)隔开。除了table外, 其他的参数都不是必须的, 分隔符的默认值是空字符, start的默认值是1, end的默认值是数组部分的总长.
sep, start, end这三个参数是顺序读入的, 所以虽然它们都不是必须参数, 但如果要指定靠后的参数, 必须同时指定前面的参数.
> tbl = {"alpha", "beta", "gamma"}
> print(table.concat(tbl, ":"))
alpha:beta:gamma
> print(table.concat(tbl, nil, 1, 2))
alphabeta
> print(table.concat(tbl, "\n", 2, 3))
beta
gamma
table.insert(table, pos, value)
table.insert()函数在table的数组部分指定位置(pos)插入值为value的一个元素. pos参数可选, 默认为数组部分末尾.
> tbl = {"alpha", "beta", "gamma"}
> table.insert(tbl, "delta")
> table.insert(tbl, "epsilon")
> print(table.concat(tbl, ", ")
alpha, beta, gamma, delta, epsilon
> table.insert(tbl, 3, "zeta")
> print(table.concat(tbl, ", ")
alpha, beta, zeta, gamma, delta, epsilon
table.maxn(table)
table.maxn()函数返回指定table中所有正数key值中最大的key值. 如果不存在key值为正数的元素, 则返回0. 此函数不限于table的数组部分.
> tbl = {[1] = "a", [2] = "b", [3] = "c", [26] = "z"}
> print(#tbl)
3 -- 因为26和之前的数字不连续, 所以不算在数组部分内
> print(table.maxn(tbl))
26
> tbl[91.32] = true
> print(table.maxn(tbl))
91.32
table.remove(table, pos)
table.remove()函数删除并返回table数组部分位于pos位置的元素. 其后的元素会被前移. pos参数可选, 默认为table长度, 即从最后一个元素删起.
table.sort(table, comp)
table.sort()函数对给定的table进行升序排序.
> tbl = {"alpha", "beta", "gamma", "delta"}
> table.sort(tbl)
> print(table.concat(tbl, ", "))
alpha, beta, delta, gamma
comp是一个可选的参数, 此参数是一个外部函数, 可以用来自定义sort函数的排序标准.
此函数应满足以下条件: 接受两个参数(依次为a, b), 并返回一个布尔型的值, 当a应该排在b前面时, 返回true, 反之返回false.
例如, 当我们需要降序排序时, 可以这样写:
> sortFunc = function(a, b) return b < a end
> table.sort(tbl, sortFunc)
> print(table.concat(tbl, ", "))
gamma, delta, beta, alpha
用类似的原理还可以写出更加复杂的排序函数. 例如, 有一个table存有工会三名成员的姓名及等级信息:
guild = {}
table.insert(guild, {
name = "Cladhaire",
class = "Rogue",
level = 70,
})
table.insert(guild, {
name = "Sagart",
class = "Priest",
level = 70,
})
table.insert(guild, {
name = "Mallaithe",
class = "Warlock",
level = 40,
})
对这个table进行排序时, 应用以下的规则: 按等级升序排序, 在等级相同时, 按姓名升序排序.
可以写出这样的排序函数:
function sortLevelNameAsc(a, b)
if a.level == b.level then
return a.name < b.name
else
return a.level < b.level
end
end
测试功能如下:
> table.sort(guild, sortLevelNameAsc)
> for idx, value in ipairs(guild) do print(idx, value.name) end
1, Mallaithe
2, Cladhaire
3, Sagart
table.foreachi(table, function(i, v))
会期望一个从 1(数字 1)开始的连续整数范围,遍历table中的key和value逐对进行function(i, v)操作
t1 = {2, 4, 6, language="Lua", version="5", 8, 10, 12, web="hello lua"};
table.foreachi(t1, function(i, v) print (i, v) end) ; --等价于foreachi(t1, print)
输出结果:
1 2
2 4
3 6
4 8
5 10
6 12
table.foreach(table, function(i, v))
与foreachi不同的是,foreach会对整个表进行迭代
t1 = {2, 4, 6, language="Lua", version="5", 8, 10, 12, web="hello lua"};
table.foreach(t1, function(i, v) print (i, v) end) ;
输出结果:
1 2
2 4
3 6
4 8
5 10
6 12
web hello lua
language Lua
version 5
table.getn(table)
返回table中元素的个数
t1 = {1, 2, 3, 5};
print(getn(t1))
->4
table.setn(table, nSize)
设置table中的元素个数
dofile (filename)
打开该名字的文件,并执行文件中的 Lua 代码块。 不带参数调用时, dofile 执行标准输入的内容(stdin)。 返回该代码块的所有返回值。 对于有错误的情况,dofile 将错误反馈给调用者 (即,dofile 没有运行在保护模式下)。
例子
同一目录下新建两个 Lua 文件,代码及输出:
-- another.lua
return "Message from another file!"
-- sample.lua
x = dofile("./another.lua")
print(x) --> Message from another file!
ipairs (t)
返回三个值:迭代器、传入的表 t、值 0 。迭代器能够根据传入的表 t 和索引 i 得到 i+1 和 t[i+1] 的值。
其实现形式类似于这样:
例子
使用 ipairs 对表进行遍历,会从键值为 1 开始依次向后遍历,直到值为 nil。
t = {"1","2",nil,[4]="4"}
-- t = {"1","2",[4]="4"} -- 使用该表会得到相同的输出
for i,v in ipairs(t) do
print(i,v)
end
--> 1 1
--> 2 2
pairs (t)
返回三个值:next 函数,表 t,nil。通常用来遍历表中的所有键值对。
如果 t 有元方法 __pairs ,将 t 作为参数 传入该函数并返回前三个返回值。
在使用 pairs 函数遍历表的过程中,可以删除域或者修改已有的域,但是如果添加新的域,可能出现无法预期的问题。
例子
t = {"table",["a"] = 5, ["c"] = 6}
for k,v in pairs(t) do
print(k,v)
end
--> 1 table
--> a 5
--> c 6
rawequal (v1, v2)
以 raw 作为前缀的函数均表示该方法在不触发任何元方法的情况下调用。
rawequal 检查 v1 是否与 v2 相等,返回比较结果。
例子
t = {"value"}
s = "value"
s2 = "value"
print(rawequal(t, s)) --> false
print(rawequal(s, s2)) --> true
rawget (table, index)
获取 table 中键 index 的关联值,table 参数必须是一个表,找不到返回 nil 。
例子
t = {"value",x = 5}
print(rawget(t, 1)) --> value
print(rawget(t, "x")) --> 5
print(rawget(t, 2)) --> nil
print(rawget("value",1))--> bad argument #1 to 'rawget' (table expected, got string)
rawset (table, index, value)
将 table[index] 的值设置为 value 。table 必须是一张表,index 不能是 nil 或 NaN 。value 可以是任何值。返回修改后的 table 。
例子
t = {"value",x = 5}
t2 = {"sub table"}
rawset(t, 1,"new value")
rawset(t, "y", 6)
rawset(t, t2,"sub table")
rawset(t,NaN,"NaN") --> table index is nil
print(t[1]) --> new value
print(t.y) --> 6
print(t[t2]) --> sub table
tonumber (e [, base])
tonumber([值],[基数]=10)
尝试把 e 转换为十进制数值并返回。如果无法转换返回 nil 。
base 表示传入参数的进制,默认为 10 进制。base 的可输入范围 [2,36]。高于 10 的数字用字母表示,A-Z 分别表示 11-35 。
例子
print(tonumber(123)) --> 123
print(tonumber("123")) --> 123
print(tonumber("abc")) --> nil
print(tonumber("abc", 20)) --> 4232
print(tonumber("ABC", 20)) --> 4232
tostring (e)
能将任意类型的值转换为合适的字符串形式返回。要控制数字转换为字符串的方式,使用 string.format(formatstring,...) 。
如果值所关联的元表有 __tostring 域,则使用该域的元方法获取字符串。
例子
function func()
print("this is a function")
end
t = {name = "table"}
print(tostring(123)) --> 123
print(tostring("abc")) --> abc
print(tostring(func)) --> function: 0x7f86348013b0
print(tostring(t)) --> table: 0x7f86348013e0
type (v)
返回 v 的类型,类型以字符串形式返回。 有以下八种返回值: "nil" , "number", "string", "boolean", "table", "function", "thread", "userdata"。
例子
type(nil) --> "nil"
type(false) --> "boolean"
type(123) --> "number"
type("abc") --> "string"
print(type(nil) == "nil") --> true
unpack (list [, i [, j]])
unpack([列表],[起始位置]=1,[返回个数]=[列表长度])
返回表中的各个域的值,等价于返回
return list[i], list[i+1], ···, list[j]
例子
t = {1,2,3,a = 4,b = 5}
print(unpack(t, 1, 4)) --> 1 2 3 nil