lua语言:类型,基本语法,函数
类型和变量
Booleans
两个取值 false 和 true。但要注意 Lua 中所有的值都可以作为条件。在控制结构的条
件中除了 false 和 nil 为假,其他值都为真。所以 Lua 认为 0 和空串都是真。
Numbers
表示实数,Lua 中没有整数。一般有个错误的看法 CPU 运算浮点数比整数慢。事实
不是如此,用实数代替整数不会有什么误差(除非数字大于 100,000,000,000,000)。Lua
的 numbers 可以处理任何长整数不用担心误差。你也可以在编译 Lua 的时候使用长整型
或者单精度浮点型代替 numbers,在一些平台硬件不支持浮点数的情况下这个特性是非
常有用的,具体的情况请参考 Lua 发布版所附的详细说明。和其他语言类似,数字常量
的小数部分和指数部分都是可选的,数字常量的例子:
4 0.4 4.57e-3 0.3e12 5e+20
Strings
指字符的序列。lua 是 8 位字节,所以字符串可以包含任何数值字符,包括嵌入的 0。
这意味着你可以存储任意的二进制数据在一个字符串里。Lua 中字符串是不可以修改的,
你可以创建一个新的变量存放你要的字符串,如下:
a = "one string" b = string.gsub(a, "one", "another") -- change string parts print(a) --> one string print(b) --> another string
string 和其他对象一样,Lua 自动进行内存分配和释放,一个 string 可以只包含一个
字母也可以包含一本书,Lua 可以高效的处理长字符串,1M 的 string 在 Lua 中是很常见
的。可以使用单引号或者双引号表示字符串
a = “a line”
b = ‘another line’
运行时,Lua 会自动在 string 和 numbers 之间自动进行类型转换,当一个字符串使
用算术操作符时,string 就会被转成数字。
print("10" + 1) --> 11 print("10 + 1") --> 10 + 1 print("-5.3e - 10" * "2") --> -1.06e-09 print("hello" + 1) -- ERROR (cannot convert "hello")
反过来,当 Lua 期望一个 string 而碰到数字时,会将数字转成 string。
print(10 … 20) --> 1020
…在 Lua 中是字符串连接符,当在一个数字后面写…时,必须加上空格以防止被解释
错
尽管字符串和数字可以自动转换,但两者是不同的,像 10 == "10"这样的比较永远
都是错的。如果需要显式将 string 转成数字可以使用函数 tonumber(),如果 string 不是正
确的数字该函数将返回 nil。
line = io.read() -- read a line n = tonumber(line) -- try to convert it to a number if n == nil then error(line .. " is not a valid number") else print(n*2) end
反之,可以调用 tostring()将数字转成字符串,这种转换一直有效:
print(tostring(10) == "10") --> true print(10 .. "" == "10") --> true
想要输出正确数字后面要接空格,再接…
表达式
关系运算
< > <= >= == ~=
不等~=
逻辑运算符
and or not
逻辑运算符认为 false 和 nil 是假(false),其他为真,0 也是 true.
and 和 or 的运算结果不是 true 和 false,而是和它的两个操作数相关。
a and b
– 如果 a 为 false,则返回 a,否则返回 b
a or b
– 如果 a 为 true,则返回 a,否则返回 b
–例如:
print(4 and 5) --> 5 print(nil and 13) --> nil print(false and 13) --> false print(4 or 5) --> 4 print(false or 5) --> 5
一个很实用的技巧:如果 x 为 false 或者 nil 则给 x 赋初始值 v
x = x or v
C 语言中的三元运算符
a ? b : c
在 Lua 中可以这样实现:
(a and b) or c
基本语法
赋值语句
遇到赋值语句 Lua 会先计算右边所有的值然后再执行赋值操作,所以我们可以这样
进行交换变量的值:
x, y = y, x
– swap ‘x’ for ‘y’
a[i], a[j] = a[j], a[i]
– swap ‘a[i]’ for ‘a[i]’
当变量个数和值的个数不一致时,Lua 会一直以变量个数为基础采取以下策略:
a. 变量个数 > 值的个数
按变量个数补足 nil
b. 变量个数 < 值的个数
多余的值会被忽略
控制结构语句
控制结构的条件表达式结果可以是任何值,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 condition do
statements;
end;
repeat-until 语句
repeat
statements;
until conditions;
for 语句有两大类:
第一,数值 for 循环:
for var=exp1,exp2,exp3 do
loop-part
end
for 将用 exp3 作为 step 从 exp1(初始值)到 exp2(终止值),执行 loop-part。其中
exp3 可以省略,默认 step=1
如果想颠倒来
for i=10,1,-1 do print(i) end -- print all values of array 'a' for i,v in ipairs(a) do print(v) end
范型 for 遍历迭代子函数返回的每一个值。
再看一个遍历表 key 的例子:
– print all keys of table ‘t’
for k in pairs(t) do print(k) end
函数
多返回值
函数多值返回的特殊函数 unpack,接受一个数组作为输入参数,返回数组的所有元
素。unpack 被用来实现范型调用机制,在 C 语言中可以使用函数指针调用可变的函数,
可以声明参数可变的函数,但不能两者同时可变。在 Lua 中如果你想调用可变参数的可
变函数只需要这样:
f(unpack(a))
unpack 返回 a 所有的元素作为 f()的参数
f = string.find a = {"hello", "ll"} print(f(unpack(a))) --> 3 4
string.find 默认情况下返回两个值, 即查找到的子串的 起下标标和止下标
预定义的 unpack 函数是用 C 语言实现的,我们也可以用 Lua 来完成:
function unpack(t, i) i = i or 1 if t[i] then return t[i], unpack(t, i + 1) end end
相当于把表的每一个item都作为参数依次传入
可变参数
Lua 函数可以接受可变数目的参数,和 C 语言类似在函数参数列表中使用三点(…)
表示函数有可变的参数。Lua 将函数的参数放在一个叫 arg 的表中,除了参数以外,arg
表中还有一个域 n 表示参数的个数。
例如,我们可以重写 print 函数:
printResult = "" function print(...) for i,v in ipairs(arg) do printResult = printResult .. tostring(v) .. "\t" end printResult = printResult .. "\n" end
如果是传入…,在函数内使用arg表
function g (a, b, ...) end g(3) a=3, b=nil, arg={n=0} g(3, 4) a=3, b=4, arg={n=0} g(3, 4, 5, 8) a=3, b=4, arg={5, 8; n=2}
域 n 表示参数的个数
举个具体的例子,如果我们只想要 string.find 返回的第二个值。一个典型的方法是
使用哑元(dummy variable,下划线):
local _, x = string.find(s, p)
– now use `x’
再论函数
a = {p = print}
a.p(“Hello World”)
–> Hello World
print = math.sin – print' now refers to the sine function a.p(print(1)) --> 0.841470 sin = a.p --
sin’ now refers to the print function
sin(10, 20)
–> 10 20
table.sort
内部是快速排序法实现
table 标准库提供一个排序函数,接受一个表作为输入参数并且排序表中的元素。这
个函数必须能够对不同类型的值(字符串或者数值)按升序或者降序进行排序。Lua 不
是尽可能多地提供参数来满足这些情况的需要,而是接受一个排序函数作为参数(类似
C++的函数对象),排序函数接受两个排序元素作为输入参数,并且返回两者的大小关系,
例如:
network = { {name = "grauna", IP = "210.26.30.34"}, {name = "arraial", IP = "210.26.30.23"}, {name = "lua", IP = "210.26.23.12"}, {name = "derain", IP = "210.26.23.20"}, } for i,v in ipairs(network) do print(v.name) end table.sort(network, function (a,b) return (a.name > b.name) end) for i,v in ipairs(network) do print(v.name) end
输出
grauna
arraial
lua
derain
lua
grauna
derain
arraial
- table中不能有nil
table.sort是排序函数,它要求要排序的目标table的必须是从1到n连续的,即中间不能有nil。 - 重写的比较函数,两个值相等时不能return true
此外,当比较函数没有写的时候,table.sort默认按照lua里面的排序规则升序排序;
当额外写了比较函数时,相当于用你额外写的比较函数重载了lua中自带的“<”操作符。
这就有一个特别要注意的问题,当两个数相等的时候,比较函数一定要返回false!
如果两个值相等都,
排序函数返回true时则会报错 invalid order function for sorting
table.sort(tmpQueue, function(a, b) if (a == nil or b == nil) then return (a.endTime < b.endTime) --此处千万不能用小于等于,不然顺序错乱 end)
network = { {name = "grauna", IP = "210.26.30.34"}, {name = "arraial", IP = "210.26.30.23"}, {name = "lua", IP = "210.26.23.12"}, {name = "grauna", IP = "210.26.23.20"}, } for i,v in ipairs(network) do print(v.name) end table.sort(network, function (a,b) return (a.name >= b.name) end) for i,v in ipairs(network) do print(v.name) end
输出
grauna
arraial
lua
grauna
lua
grauna
arraial
grauna
闭包
函数内部有函数
在匿名函数内部 grades 不是全局变量也不是局部变量,我们称作外部的局部变
量(external local variable)或者 upvalue。
function newCounter() local i = 0 return function() -- anonymous function i = i + 1 return i end end c1 = newCounter() print(c1()) --> 1 print(c1()) --> 2 c2 = newCounter() print(c2()) --> 1 print(c1()) --> 3 print(c2()) --> 2
匿名函数使用 upvalue i 保存他的计数,当我们调用匿名函数的时候 i 已经超出了作
用范围,因为创建 i 的函数 newCounter 已经返回了。然而 Lua 用闭包的思想正确处理了
这种情况。简单的说,闭包是一个函数以及它的 upvalues。如果我们再次调用 newCounter,
将创建一个新的局部变量 i,因此我们得到了一个作用在新的变量 i 上的新闭包。
闭包在完全不同的上下文中也是很有用途的。因为函数被存储在普通的变量内我们
可以很方便的重定义或者预定义函数。通常当你需要原始函数有一个新的实现时可以重
定义函数。例如你可以重定义 sin 使其接受一个度数而不是弧度作为参数:
do local oldSin = math.sin local k = math.pi/180 math.sin = function (x) return oldSin(x*k) end end
利用同样的特征我们可以创建一个安全的环境(也称作沙箱,和 java 里的沙箱一样),
当我们运行一段不信任的代码(比如我们运行网络服务器上获取的代码)时安全的环境
是需要的,比如我们可以使用闭包重定义 io 库的 open 函数来限制程序打开的文件。
do local oldOpen = io.open io.open = function (filename, mode) if access_OK(filename, mode) then return oldOpen(filename, mode) else return nil, "access denied" end end end
非全局函数
递归函数先声明
上面这种方式导致 Lua 编译时遇到 fact(n-1)并不知道他是局部函数 fact,Lua 会去查
找是否有这样的全局函数 fact。为了解决这个问题我们必须在定义函数以前先声明:
local fact fact = function (n) if n == 0 then return 1 else return n*fact(n-1) end end
迭代器与泛型for
迭代器与闭包
迭代器是一种支持指针类型的结构,它可以遍历集合的每一个元素
举一个简单的例子,我们为一个 list 写一个简单的迭代器,与 ipairs()不同的是我们
实现的这个迭代器返回元素的值而不是索引下标:
function list_iter (t) local i = 0 local n = table.getn(t) return function () i = i + 1 if i <= n then return t[i] end end end t = {10, 20, 30} for element in list_iter(t) do print(element) end
可以自己写些遍历的条件,例如取table里满足条件的item
范性for
ipairs与pairs区别
ipairs 仅仅遍历值,按照索引升序遍历,索引中断停止遍历。即不能返回 nil,只能返回数字 0,如果遇到 nil 则退出。它只能遍历到集合中出现的第一个不是整数的 key。必须是连续的,从1开始,只要中间为nil,即断开
pairs 能遍历集合的所有元素。即 pairs 可以遍历集合中所有的 key,并且除了迭代器本身以及遍历表本身还可以返回 nil。
local tab= { [1] = "a", [3] = "b", [4] = "c" } for i,v in pairs(tab) do -- 输出 "a" ,"b", "c" , print( tab[i] ) end for i,v in ipairs(tab) do -- 输出 "a" ,k=2时断开 print( tab[i] ) end
编译,运行,错误信息
assert断言
local f = assert(loadstring("return " … l))
require 函数
Lua 提供高级的 require 函数来加载运行库。粗略的说 require 和 dofile 完成同样的功
能但有两点不同:
- require 会搜索目录加载文件
- require 会判断是否文件已经加载避免重复加载同一文件。由于上述特征,require
在 Lua 中是加载库的更好的函数。
require 的另一个功能是避免重复加载同一个文件两次。Lua 保留一张所有已经加载
的文件的列表(使用 table 保存)。如果一个加载的文件在表中存在 require 简单的返回;
表中保留加载的文件的虚名,而不是实文件名。所以如果你使用不同的虚文件名 require
同一个文件两次,将会加载两次该文件。比如 require "foo"和 require “foo.lua”,路径为
"?;?.lua"将会加载 foo.lua 两次。
C Package
local path = “/usr/local/lua/lib/libluasocket.so”
– or path = “C:\windows\luasocket.dll”
local f = assert(loadlib(path, “luaopen_socket”))
f() – actually open the library
异常与错误处理
lua实现try catch
当我们的Lua程序遇到有需要保护的代码或者方法时(即使程序异常,也只是抛出异常信息,而不是让程序崩溃),Lua为我们提供了两种解决的办法,这两种方法可以让我们捕获异常,因此封装自己的tryCatch函数。
1.pcall调用
2.xpcall调用
相同点:
当程序正常时,返回true,被执行函数的返回值
不同点:
1.参数不同
pcall(fun) ,参数只有一个被调用函数
xpcall(fun,errHandleFun),参数是被调用函数,错误函数处理
2.执行结果
pcall:返回错误信息时,已经释放了保存错误发生情况的栈信息。
xpcall:会在栈信息释放之前调用错误处理程序(可以使用debug库收集错误信息)
3.返回结果
pcall 返回 nil , 错误信息
xpcall返回nil , 无错误信息
local fun=function ( b) local a=1; print(a+b); return a+b; end tryCatch=function(fun) local ret,errMessage=pcall(fun); print("ret:" .. (ret and "true" or "false" ) .. " \nerrMessage:" .. (errMessage or "null")); end xTryCatchGetErrorInfo=function() print(debug.traceback()); end xTryCatch=function(fun) local ret,errMessage=xpcall(fun,xTryCatchGetErrorInfo); print("ret:" .. (ret and "true" or "false" ) .. " \nerrMessage:" .. (errMessage or "null")); end print("\n------A------\n") tryCatch(fun("1")); print("\n------B------\n") xTryCatch(fun("1")); print("\n------C------\n")
输出
------A------
2
ret:false
errMessage:attempt to call a number value
------B------
2
stack traceback:
PInLua.lua:13: in function PInLua.lua:12
[C]: in function ‘xpcall’
PInLua.lua:16: in function ‘xTryCatch’
PInLua.lua:25: in main chunk
[C]: ?
ret:false
errMessage:null
------C------
协同程序
Lua中的协程和unity协程的区别,最大的就是其不是抢占式的执行,也就是说不会被主动执行类似MoveNext这样的操作,而是需要我们去主动激发执行,就像上一个例子一样,自己去tick这样的操作。
Lua中协程关键的三个API:
coroutine.create()/wrap: 构建一个协程, wrap构建结果为函数,create为thread类型对象
coroutine.resume(): 执行一次类似MoveNext的操作
coroutine.yield(): 将协程挂起
比较简易,可以写也给例子测试一下:
local func = function(a, b) for i= 1, 3 do print(i, a, b) end end local func1 = function(a, b) for i = 1, 3 do print(i, a, b) coroutine.yield() end end co = coroutine.create(func) coroutine.resume(co, 1, 2) --此时会输出 1 ,1, 2/ 2,1,2/ 3, 1,2 co1 = coroutine.create(func1) coroutine.resume(co1, 1, 2) --此时会输出 1, 1,2 然后挂起 coroutine.resume(co1, 3, 4) --此时将上次挂起的协程恢复执行一次,输出: 2, 1, 2 所以新传入的参数3,4是无效的 coroutine.resume(co1, 3, 4) coroutine.resume(co1, 3, 4) coroutine.resume(co1, 3, 4) coroutine.resume(co1, 3, 4)
输出
1 1 2
2 1 2
3 1 2
1 1 2
2 1 2
3 1 2
posted on 2021-10-31 00:51 luoyikun 阅读(16) 评论(0) 编辑 收藏 举报 来源
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
2018-10-31 Android Studio快捷键