luoyikun

导航

统计

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

  1. table中不能有nil
    table.sort是排序函数,它要求要排序的目标table的必须是从1到n连续的,即中间不能有nil。
  2. 重写的比较函数,两个值相等时不能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 完成同样的功
能但有两点不同:

  1. require 会搜索目录加载文件
  2. 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   luoyikun  阅读(16)  评论(0编辑  收藏  举报  

相关博文:
阅读排行:
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
历史上的今天:
2018-10-31 Android Studio快捷键
点击右上角即可分享
微信分享提示