# 什么是Lua编程

以下部分内容引用自:https://www.runoob.com/lua/

# lua语言简介

概述

Lua是一种轻量小巧的脚本语言,用标准C语言编写并以源代码形式开放, 其设计目的是为了嵌入应用程序中,从而为应用程序提供灵活的扩展和定制功能。

特点

轻量级、可扩展,等等

  • 轻量级: 它用标准C语言编写并以源代码形式开放,编译后仅仅一百余K,可以很方便的嵌入别的程序里。
  • 可扩展: Lua提供了非常易于使用的扩展接口和机制:由宿主语言(通常是C或C++)提供这些功能,Lua可以使用它们,就像是本来就内置的功能一样。
  • 其它特性:
    • 支持面向过程(procedure-oriented)编程和函数式编程(functional programming);
    • 自动内存管理;只提供了一种通用类型的表(table),用它可以实现数组,哈希表,集合,对象;
    • 语言内置模式匹配;闭包(closure);函数也可以看做一个值;提供多线程(协同进程,并非操作系统所支持的线程)支持;
    • 通过闭包和table可以很方便地支持面向对象编程所需要的一些关键机制,比如数据抽象,虚函数,继承和重载等。

应用场景:

  1. 游戏开发
  2. 独立应用脚本
  3. Web 应用脚本
  4. 扩展和数据库插件如:MySQL Proxy 和 MySQL WorkBench
  5. 安全系统,如入侵检测系统

注意:

以下的案例,为了方便演示,都使用Chat:sendSystemMsg接口,把需要查看的信息都输出在游戏的聊天框内! 复制代码到游戏中,可以在聊天框看到反馈。

# Lua基本语法

注释

单行注释符: --

-- 这里的内容就不会执行了

注释

多行注释:

--[[
被包含的区域都不会执行
--]]

标识符

什么是标识符:

1. Lua 标示符用于定义一个变量,函数获取其他用户定义的项。
2. 标示符以一个字母 A 到 Z 或 a 到 z 或下划线 _ 开头后加上0个或多个字母,下划线,数字(0到9)。
3. 最好不要使用下划线加大写字母的标示符,因为Lua的保留字也是这样的。
4. Lua 不允许使用特殊字符如 @, $, 和 % 来定义标示符。
5. 因此在 Lua 中 Miniworld 与 miniworld 是两个不同的标示符

标识符

正确写法:

mini   world   abc   mini_world  a_123
mini521   _mini  c   mi5ni2   MiNi

关键词

以下列出了 Lua 的保留关键字。保留关键字不能作为常量或变量或其他用户自定义标示符:

and break do else elseif end false for function if
in local nil not or repeat return then true until
while goto

全局变量

在默认情况下,变量总是认为是全局的。 全局变量不需要声明,给一个变量赋值后即创建了这个全局变量,访问一个没有初始化的全局变量也不会出错,只不过得到的结果是:nil(空值)。

Chat:sendSystemMsg(b , 0) --未声明的变量b,不会报错,但把b的值打印出来是nil的
------------------
b=10
Chat:sendSystemMsg(b , 0) --声明的变量b,b的值打印出来是10
------------------
--如果你想删除一个全局变量,只需要将变量赋值为nil。
b = nil
Chat:sendSystemMsg(b , 0)
--这样变量b就好像从没被使用过一样。换句话说, 当且仅当一个变量不等于nil时,这个变量即存在。

# Lua数据类型

数据名称 用法描述
nil 表示一个无效值(在条件表达式中相当于false)
boolean 布尔值,包含两个值:false和true
number 实数,表示双精度类型的实浮点数,也可以是整数
string 字符串,由一对单引号或双引号来表示
table Lua的一种数据结构,可用来创建不同的数据类型,如:数组、字典等
function 由 C 或 Lua 编写的函数
userdata 表示任意存储在变量中的C数据结构
thread 表示执行的独立线路,用于执行协同程序

# Lua的变量

变量概述

什么是变量?

  1. 变量在使用前,必须在代码中进行声明,即创建该变量。
  2. 编译程序执行代码之前编译器需要知道如何给语句变量开辟存储区,用于存储变量的值。
  3. Lua 变量有三种类型:全局变量、局部变量、表中的域。
  4. Lua 中的变量全是全局变量,那怕是语句块或是函数里,除非用 local 显式声明为局部变量。
  5. 局部变量的作用域为从声明位置开始到所在语句块结束。
  6. 变量的默认值均为 nil。
 




















a = 5               -- 全局变量
local b = 5         -- 局部变量
--------------
function joke()		-- 定义函数
    c = 5           -- 全局变量
    local d = 6     -- 局部变量
end
--------------
joke()
Chat:sendSystemMsg(c,0)          --> 5
Chat:sendSystemMsg(d,0)          --> nil


do 
    local a = 6     -- 局部变量
    b = 6           -- 对局部变量重新赋值
    Chat:sendSystemMsg(a,0);     --> 6
    Chat:sendSystemMsg(b,0);     --> 6
end
print(a,b)      --> 5 6

赋值语句

赋值是改变一个变量的值和改变表域的最基本的方法。

a = "hello" .. "world"
--多个变量同时赋值,变量列表和值列表各元素用逗号分开,赋值语句右边的值会依次赋给左边变量。
a, b = 10, 2*x 
--遇到赋值语句Lua会先计算右边所有的值然后再执行赋值操作,所以我们可以这样进行交换变量的值:
x, y = y, x                    
a[i], a[j] = a[j], a[i] 

注意

当变量个数和值的个数不一致时,Lua会一直以变量个数为基础采取以下策略:

  1. 变量个数 > 值的个数, 按变量个数补足nil
  2. 变量个数 < 值的个数, 多余的值会被忽略
a, b, c = 0, 1
Chat:sendSystemMsg(a,0)     --> 0
Chat:sendSystemMsg(b,0)     --> 1
Chat:sendSystemMsg(c,0)     --> nil
 
a, b, c = 0, 1 
a, b = a+1, b+1, b+2  
Chat:sendSystemMsg(a,0)     --> 1
Chat:sendSystemMsg(b,0)     --> 2

a, b, c = 0
Chat:sendSystemMsg(a,0)   		   --> 0
Chat:sendSystemMsg(b,0)            --> nil
Chat:sendSystemMsg(c,0)            --> nil
--最后一个例子是一个常见的错误情况,注意:如果要对多个变量赋值必须依次对每个变量赋值。
a, b = f()
--多值赋值经常用来交换变量,或将函数调用返回给变量
--f()返回两个值,第一个赋给a,第二个赋给b。
--应该尽可能的使用局部变量,有两个好处:

--1. 避免命名冲突。
--2. 访问局部变量的速度比全局变量更快。

索引

对 table 的索引使用方括号 []。Lua 也提供了 . 操作。

t[i]
t.i                 -- 当索引为字符串类型时的一种简化写法
gettable_event(t,i) -- 采用索引访问本质上是一个类似这样的函数调用
 site = {}
 site["key"] = "www.mini1.com"
 Chat:sendSystemMsg(site["key"],0)
--例子
 Chat:sendSystemMsg(site.key , 0)

# Lua的循环语句

序号 控制语句 描述
1 while循环 在条件为 true 时,让程序重复地执行某些语句。执行语句前会先检查条件是否为 true。
2 for循环 重复执行指定语句,重复次数可在 for 语句中控制。
3 repeat...until 重复执行循环,直到 指定的条件为真时为止
4 循环嵌套 可以在循环内嵌套一个或多个循环语句(while do ... end;for ... do ... end;repeat ... until;)
序号 控制语句 描述
1 break语句 退出当前循环或语句,并开始脚本执行紧接着的语句。
2 goto语句 将程序的控制点转移到一个标签处。

while循环

--案例:
--以下代码实例在聊天框循环输出a的值
a = 0
while( a < 5 )  --当a<5的时候才会执行do里面的内容
do
   Chat:sendSystemMsg( a, 0)  --输出a的值到聊天框
   a = a+1  --每次循环a都会自身+1
end
--输出结果为:
-- 0 
-- 1
-- 2
-- 3
-- 4

for循环

数值for循环:

for var=a , b , c do
<执行体>
end

var 从 a 变化到 b,每次变化以 c 为步长递增var,并执行一次 "执行体"。c 是可选的,如果不指定,默认为1。

--案例:
--简单的for循环,可以在聊天框输出i的值:
for i = 1 , 5 , 1  do 
	Chat:sendSystemMsg( i, 0)
end 
--输出结果为:
-- 1
-- 2
-- 3
-- 4
-- 5

泛型for循环:

泛型 for 循环通过一个迭代器函数来遍历所有值,类似 java 中的 foreach 语句。

--实例:
--聊天框打印数组a的所有值  

a = {"one", "two", "three"}
for i, v in ipairs(a) do
	Chat:sendSystemMsg( v, 0)
end 

--i是数组索引值,v是对应索引的数组元素值。ipairs是Lua提供的一个迭代器函数,用来迭代数组。

repeat...until循环

概述:

Lua 编程语言中 repeat...until 循环语句不同于 for 和 while循环,for 和 while 循环的条件语句在当前循环执行开始时判断,而 repeat...until 循环的条件语句在当前循环结束后判断。

--实例:
--以下代码在聊天框输出a的值
a = 0
--[ 执行循环 --]
repeat
	Chat:sendSystemMsg( a, 0)
   a = a + 1
until( a > 5 )  --循环直到a>5
--输出结果为:
-- 0
-- 1
-- 2
-- 3
-- 4
-- 5

循环嵌套

概述

Lua 编程语言中允许循环中嵌入循环。我们还可以使用不同的循环类型来嵌套,如 for 循环体中嵌套 while 循环。

--案例:
--以下代码在聊天框输出i和j的值

for i = 1 , 2 , 1 do
   for j = 1, 2 , 1 do

		Chat:sendSystemMsg( i, 0)
		Chat:sendSystemMsg( j, 0)
      end
   end
end
--输出结果为:
-- 1
-- 1
-- 1
-- 2
-- 2
-- 1
-- 2
-- 2

break语句

break语句

  1. Lua 编程语言 break 语句插入在循环体中,用于退出当前循环或语句,并开始脚本执行紧接着的语句。
  2. 如果你使用循环嵌套,break语句将停止最内层循环的执行,并开始执行的外层的循环语句。
--以下实例执行 while 循环,在变量 a 小于 20 时输出 a 的值,并在 a 大于 15 时终止执行循环:

--[ 定义变量 --]
a = 10
--[ while 循环 --]
while( a < 20 )
do
	Chat:sendSystemMsg( a, 0)
   a=a+1
   if( a > 15)
   then
      --[ 使用 break 语句终止循环 --]
      break
   end
end
--输出结果为:
-- 10
-- 11
-- 12
-- 13
-- 14 
-- 15

goto 语句

goto语句

Lua 语言中的 goto 语句允许将控制流程无条件地转到被标记的语句处。

local a = 1
::start:: Chat:sendSystemMsg("goto的位置",0)

a = a+1
if a < 3 then
    goto start   -- a 小于 3 的时候跳转到标签 start
end
--输出的结果:
-- goto的位置
-- goto的位置

# 流程控制语句(条件语句)

概述

  1. Lua 编程语言流程控制语句通过程序设定一个或多个条件语句来设定。在条件为 true 时执行指定程序代码,在条件为 false 时执行其他指定代码。
  2. 控制结构的条件表达式结果可以是任何值,Lua认为false和nil为假,true和非nil为真。
  3. 要注意的是Lua中 0 为 true:
--[ 0 为 true ]
if(0)
then
	Chat:sendSystemMsg("0 为 true", 0)
end
序号 语句 描述
1 if语句 if 语句 由一个布尔表达式作为条件判断,其后紧跟其他语句组成。
2 if...else语句 if 语句 可以与 else 语句搭配使用, 在 if 条件表达式为 false 时执行 else 语句代码。
3 if 嵌套语句 你可以在if 或 else if中使用一个或多个 if 或 else if 语句 。。

if语句

if语句

Lua if 语句 由一个布尔表达式作为条件判断,其后紧跟其他语句组成。

--以下实例用于判断变量 a 的值是否小于 20:
--[ 定义变量 --]
a = 10;

--[ 使用 if 语句 --]
if( a < 20 )
then
   --[ if 条件为 true 时打印以下信息 --]
   Chat:sendSystemMsg("a 小于 20" , 0);
end

Chat:sendSystemMsg( "a的值为:"..a.."", 0);

--输出的结果为:
--a小于20
--a的值为:10

if...else语句

if...else语句

Lua的if 语句可以与 else 语句搭配使用, 在 if 条件表达式为 false 时执行 else 语句代码块。

--以下实例用于判断变量 a 的值:
a = 100;
--[ 检查条件 --]
if( a < 20 )
then
   --[ if 条件为 true 时执行该语句块 --]
   Chat:sendSystemMsg("a 小于 20",0 )
else
   --[ if 条件为 false 时执行该语句块 --]
   Chat:sendSystemMsg("a 大于 20",0 )
end
Chat:sendSystemMsg("a的值为 :"..a.."", 0)

--输出的结果为:
--a 大于 20
--a的值为:100

if...elseif...else 语句

Lua的if 语句可以与 elseif...else 语句搭配使用, 在 if 条件表达式为 false 时执行 elseif...else 语句代码块,用于检测多个条件语句。

--以下实例对变量 a 的值进行判断:
--[ 定义变量 --]
a = 100

--[ 检查布尔条件 --]
if( a == 10 )
then
   --[ 如果条件为 true 打印以下信息 --]
   Chat:sendSystemMsg("a 的值为 10" ,0)
elseif( a == 20 )
then   
   --[ if else if 条件为 true 时打印以下信息 --]
   Chat:sendSystemMsg("a 的值为 20" ,0)
elseif( a == 30 )
then
   --[ if else if condition 条件为 true 时打印以下信息 --]
   Chat:sendSystemMsg("a 的值为 30" ,0)
else
   --[ 以上条件语句没有一个为 true 时打印以下信息 --]
   Chat:sendSystemMsg("没有匹配 a 的值" ,0)
end
Chat:sendSystemMsg("a 的值为 :".. a .."", 0)

if嵌套语句

if嵌套语句

Lua 的if 语句允许嵌套, 这就意味着你可以在一个 if 或 else if 语句中插入其他的 if 或 else if 语句。

--以下实例用于判断变量 a 和 b 的值,并输出在聊天框:
--[ 定义变量 --]
a = 100;
b = 200;

--[ 检查条件 --]
if( a == 100 )
then
   --[ if 条件为 true 时执行以下 if 条件判断 --]
   if( b == 200 )
   then
      --[ if 条件为 true 时执行该语句块 --]
      Chat:sendSystemMsg("a 的值为 100 ,b 的值为 200" );
   end
end
Chat:sendSystemMsg("a 的值为 :" .. a.."", 0);
Chat:sendSystemMsg("b 的值为 :".. b .."", 0 );

# Lua函数

什么是函数

在Lua中,函数是对语句和表达式进行抽象的主要方法。既可以用来处理一些特殊的工作,也可以用来计算一些值。

  • Lua 提供了许多的内建函数,你可以很方便的在程序中调用它们,如print()函数可以将传入的参数打印在控制台上。
  • Lua 函数主要有两种用途:
    • 完成指定的任务,这种情况下函数作为调用语句使用;
    • 计算并返回值,这种情况下函数作为赋值语句的表达式使用。

函数定义

Lua 编程语言函数定义格式如下:

optional_function_scope function function_name( argument1, argument2, argument3..., argumentn)
    function_body
    return result_params_comma_separated
end

代码解析:

  • optional_function_scope: 该参数是可选的制定函数是全局函数还是局部函数,未设置该参数默认为全局函数,如果你需要设置函数为局部函数需要使用关键字 local。
  • function_name: 指定函数名称。
  • argument1, argument2, argument3..., argumentn: 函数参数,多个参数以逗号隔开,函数也可以不带参数。
  • function_body: 函数体,函数中需要执行的代码语句块。
  • result_params_comma_separated: 函数返回值,Lua语言函数可以返回多个值,每个值以逗号隔开。

案例1:

以下实例定义了函数 max(),参数为 num1, num2,用于比较两值的大小,并返回最大值:

--[[ 函数返回两个值的最大值 --]]
function max(num1, num2)

   if (num1 > num2) then
      result = num1;
   else
      result = num2;
   end

   return result; 
end
-- 调用函数
Chat:sendSystemMsg("两值比较最大值为:"..max(10,4).."",0)
Chat:sendSystemMsg("两值比较最大值为:"..max(5,6).."",0)

案例2: Lua 中我们可以将函数作为参数传递给函数,如下实例:

myprint = function(param)
   Chat:sendSystemMsg("param的值:" ..param.. "",0)

end

function add(num1,num2,functionPrint)
   result = num1 + num2
   -- 调用传递的函数参数
   functionPrint(result)
end
myprint(10)
-- myprint 函数作为参数传递
add(2,5,myprint)

多返回值

Lua函数可以返回多个结果值,比如string.find,其返回匹配串"开始和结束的下标"(如果不存在匹配串返回nil)。

案例: Lua函数中,在return后列出要返回的值的列表即可返回多值,如:

function maximum (a)
    local mi = 1             -- 最大值索引
    local m = a[mi]          -- 最大值
    for i,val in ipairs(a) do
       if val > m then
           mi = i
           m = val
       end
    end
    return m, mi
end
print(maximum({8,10,23,12,5}))
--由于Chat:sendSystemMsg输出机制只能输出一个值,只能用print打印来看输出的返回值

可变参数

Lua函数可以接受可变数目的参数,和 C 语言类似,在函数参数列表中使用三点 ... 表示函数有可变的参数。

案例1:拼接

-- 上段代码由于Chat:sendSystemMsg只能输出1个值,所以无法输出函数的两个返回值
-- 我们可以用可变参数来做拼接功能
function dump_args(...)
        local buffer = ''
        local size = select('#', ...)
    for idx = 1, select('#', ...) do
            local arg = select(idx, ...)
            local ends = idx==size and "" or ", "
            buffer = buffer..tostring(arg)..ends
    end
    return buffer
end
function maximum (a)
    local mi = 1             -- 最大值索引
    local m = a[mi]          -- 最大值
    for i,val in ipairs(a) do
       if val > m then
           mi = i
           m = val
       end
    end
    return m, mi
end

Chat:sendSystemMsg(dump_args(maximum({8,10,23,12,5})),0)
--这样调用可变参数dump_args,就可以打印出多个函数返回值了

案例2:求平均值

function average(...)
   result = 0
   local arg={...}    --> arg 为一个表,局部变量
   for i,v in ipairs(arg) do
      result = result + v
   end
   Chat:sendSystemMsg("总共传入 " .. #arg .. " 个数",0)
   return result/#arg
end
Chat:sendSystemMsg("平均值为" ..average(10,5,3,4,5,6).. " ",0)

案例3:求平均值

---我们也可以通过 select("#",...) 来获取可变参数的数量:

function average(...)
   result = 0
   local arg={...}
   for i,v in ipairs(arg) do
      result = result + v
   end
   print("总共传入 " .. select("#",...) .. " 个数")
   return result/select("#",...)
end
Chat:sendSystemMsg("平均值为"..average(10,5,3,4,5,6).." ",0)

# Lua运算符

运算符释义:

运算符是一个特殊的符号,用于告诉解释器执行特定的数学或逻辑运算。Lua提供了以下几种运算符类型:

算数运算符

下表列出了Lua语言中的常用算数运算符,设定 A 的值为10,B 的值为 20:

操作符 描述 实例
+ 加法 A + B 输出结果 30
- 减法 A - B 输出结果 -10
* 乘法 A * B 输出结果 200
/ 除法 B / A w输出结果 2
% 取余 B % A 输出结果 0
^ 乘幂 A^2 输出结果 100
- 负号 -A 输出结果 -10

案例: 我们可以通过以下实例来更加透彻的理解算术运算符的应用:

a = 21
b = 10
c = a + b
print("Line 1 - c 的值为 ", c )
c = a - b
print("Line 2 - c 的值为 ", c )
c = a * b
print("Line 3 - c 的值为 ", c )
c = a / b
print("Line 4 - c 的值为 ", c )
c = a % b
print("Line 5 - c 的值为 ", c )
c = a^2
print("Line 6 - c 的值为 ", c )
c = -a
print("Line 7 - c 的值为 ", c )
--打印结果为:
--Line 1 - c 的值为     31
--Line 2 - c 的值为     11
--Line 3 - c 的值为     210
--Line 4 - c 的值为     2.1
--Line 5 - c 的值为     1
--Line 6 - c 的值为     441
--Line 7 - c 的值为     -21

关系运算符

下表列出了 Lua 语言中的常用关系运算符,设定 A 的值为10,B 的值为 20:

操作符 描述 实例
== 等于,检测两个值是否相等,相等返回 true,否则返回 false (A == B) 为 false。
~= 不等于,检测两个值是否相等,相等返回 false,否则返回 true (A ~= B) 为 true。
> 大于,如果左边的值大于右边的值,返回 true,否则返回 false (A > B) 为 false。
< 小于,如果左边的值大于右边的值,返回 false,否则返回 true (A < B) 为 true。
>= 大于等于,如果左边的值大于等于右边的值,返回 true,否则返回 false (A >= B) 返回 false。
<= 小于等于, 如果左边的值小于等于右边的值,返回 true,否则返回 false (A <= B) 返回 true。

案例: 我们可以通过以下实例来更加透彻的理解关系运算符的应用:

a = 21
b = 10

if( a == b )
then
   print("Line 1 - a 等于 b" )
else
   print("Line 1 - a 不等于 b" )
end

if( a ~= b )
then
   print("Line 2 - a 不等于 b" )
else
   print("Line 2 - a 等于 b" )
end

if ( a < b )
then
   print("Line 3 - a 小于 b" )
else
   print("Line 3 - a 大于等于 b" )
end

if ( a > b ) 
then
   print("Line 4 - a 大于 b" )
else
   print("Line 5 - a 小于等于 b" )
end

-- 修改 a 和 b 的值
a = 5
b = 20
if ( a <= b ) 
then
   print("Line 5 - a 小于等于  b" )
end

if ( b >= a ) 
then
   print("Line 6 - b 大于等于 a" )
end
--执行结果为:
--Line 1 - a 不等于 b
--Line 2 - a 不等于 b
--Line 3 - a 大于等于 b
--Line 4 - a 大于 b
--Line 5 - a 小于等于  b
--Line 6 - b 大于等于 a

逻辑运算符

操作符 描述 实例
and 逻辑与操作符。 若 A 为 false,则返回 A,否则返回 B。 (A and B) 为 false。
or 逻辑或操作符。 若 A 为 true,则返回 A,否则返回 B。 (A or B) 为 true。
not 逻辑非操作符。与逻辑运算结果相反,如果条件为 true,逻辑非为 false。 not(A and B) 为 true。

案例: 我们可以通过以下实例来更加透彻的理解逻辑运算符的应用:

a = true
b = true

if ( a and b )
then
   print("a and b - 条件为 true" )
end

if ( a or b )
then
   print("a or b - 条件为 true" )
end

print("---------分割线---------" )

-- 修改 a 和 b 的值
a = false
b = true

if ( a and b )
then
   print("a and b - 条件为 true" )
else
   print("a and b - 条件为 false" )
end

if ( not( a and b) )
then
   print("not( a and b) - 条件为 true" )
else
   print("not( a and b) - 条件为 false" )
end
--以上程序执行结果为:
--a and b - 条件为 true
--a or b - 条件为 true

--a and b - 条件为 false
--not( a and b) - 条件为 true

其他运算符

下表列出了 Lua 语言中的连接运算符与计算表或字符串长度的运算符:
操作符 描述 实例
.. 连接两个字符串 a..b ,其中 a 为 "Hello " , b 为 "World", 输出结果为 "Hello World"。
# 一元运算符,返回字符串或表的长度。 #"Hello" 返回 5

案例:

我们可以通过以下实例来更加透彻的理解连接运算符与计算表或字符串长度的运算符的应用:

a = "Hello "
b = "MiniWorld"

print("连接字符串 a 和 b ", a..b )

print("b 字符串长度 ",#b )

print("字符串 Test 长度 ",#"Test" )

print("迷你世界网址长度 ",#"https://www.mini1.cn/" )
--以上程序执行结果为:
--连接字符串 a 和 b 	Hello MiniWorld
--b 字符串长度     5
--字符串 Test 长度     4
--迷你世界网址长度 	21

运算符优先级

从高到低的顺序:

^
not    - (unary)
*      /
+      -
..
<      >      <=     >=     ~=     ==
and
or

除了 ^ 和 .. 外所有的二元运算符都是左连接的。

a+i < b/2+1          <-->       (a+i) < ((b/2)+1)
5+x^2*8              <-->       5+((x^2)*8)
a < y and y <= z     <-->       (a < y) and (y <= z)
-x^2                 <-->       -(x^2)
x^y^z                <-->       x^(y^z)

案例: 我们可以通过以下实例来更加透彻的了解 Lua 语言运算符的优先级:

a = 20
b = 10
c = 15
d = 5

e = (a + b) * c / d;-- ( 30 * 15 ) / 5
print("(a + b) * c / d 运算值为  :",e )

e = ((a + b) * c) / d; -- (30 * 15 ) / 5
print("((a + b) * c) / d 运算值为 :",e )

e = (a + b) * (c / d);-- (30) * (15/5)
print("(a + b) * (c / d) 运算值为 :",e )

e = a + (b * c) / d;  -- 20 + (150/5)
print("a + (b * c) / d 运算值为   :",e )
--以上程序执行结果为:
--(a + b) * c / d 运算值为  :    90.0
--((a + b) * c) / d 运算值为 :    90.0
--(a + b) * (c / d) 运算值为 :    90.0
--a + (b * c) / d 运算值为   :    50.0

# Lua字符串

概述:

字符串或串(String)是由数字、字母、下划线组成的一串字符。Lua 语言中字符串可以使用以下三种方式来表示:

  • 单引号间的一串字符。
  • 双引号间的一串字符。
  • [[和]]间的一串字符。 案例:
string1 = "Lua"
print("\"字符串 1 是\"",string1)
string2 = 'https://www.mini1.cn/'
print("字符串 2 是",string2)
string3 = [["Lua 教程"]]
print("字符串 3 是",string3)

--以上代码执行输出结果为:
--"字符串 1 是"    Lua
--字符串 2 是    https://www.mini1.cn/
--字符串 3 是    "Lua 教程"

转义字符用于表示不能直接显示的字符,比如后退键,回车键,等。如在字符串转换双引号可以使用 " \ ""。

转译字符 意义 ASCII码值(十进制)
\a 响铃(BEL) 007
\b 退格(BS) ,将当前位置移到前一列 008
\f 换页(FF),将当前位置移到下页开头 012
\n 换行(LF) ,将当前位置移到下一行开头 010
\r 回车(CR) ,将当前位置移到本行开头 013
\t 水平制表(HT) (跳到下一个TAB位置) 009
\v 垂直制表(VT) 011
\ 代表一个反斜线字符''' 092
' 代表一个单引号(撇号)字符 039
" 代表一个双引号字符 034
\0 空字符(NULL) 000
\ddd 1到3位八进制数所代表的任意字符 三位八进制
\xhh 1到2位十六进制所代表的任意字符 二位十六进制

字符串操作

Lua 提供了很多的方法来支持字符串的操作:
序号 方法 & 用途
1 string.upper(argument):
字符串全部转为大写字母。
2 string.lower(argument):
字符串全部转为小写字母。
3 string.gsub(mainString,findString,replaceString,num)
在字符串中替换。
mainString 为要操作的字符串, findString 为被替换的字符,replaceString 要替换的字符,num 替换次数(可以忽略,则全部替换),如:
string.gsub("aaaa","a","z",3)
zzza 3
4 string.find (str, substr, [init, [end]])
在一个指定的目标字符串中搜索指定的内容(第三个参数为索引),返回其具体位置。不存在则返回 nil。
string.find("Hello Lua user", "Lua", 1)
7 9
5 string.reverse(arg)
字符串反转
string.reverse("Lua")
auL
6 string.format(...)
返回一个类似printf的格式化字符串
string.format("the value is:%d",4)
the value is:4
7 string.char(arg) 和 string.byte(arg[,int])
char 将整型数字转成字符并连接, byte转换字符为整数值(可以指定某个字符,默认第一个字符)。
string.char(97,98,99,100)
abcd
string.byte("ABCD",4)
68
> string.byte("ABCD")
65
8 string.len(arg)
计算字符串长度。
string.len("abc")
3
9 string.rep(string, n)
返回字符串string的n个拷贝
string.rep("abcd",2)
abcdabcd
10 ..
链接两个字符串
print("www.mini1.".."cn")
www.mini1.cn
11 string.gmatch(str, pattern)
返回一个迭代器函数,每一次调用这个函数,返回一个在字符串 str 找到的下一个符合 pattern 描述的子串。如果参数 pattern 描述的字符串没有找到,迭代函数返回nil。
for word in string.gmatch("Hello Lua user", "%a+") do print(word) end
Hello
Lua
Lua
12 string.match(str, pattern, init)
string.match()只寻找源字串str中的第一个配对. 参数init可选, 指定搜寻过程的起点, 默认为1。
在成功配对时, 函数将返回配对表达式中的所有捕获结果; 如果没有设置捕获标记, 则返回整个配对字符串. 当没有成功的配对时, 返回nil。
string.match("I have 2 questions for you.", "%d+ %a+")
2 questions
string.format("%d, %q", string.match("I have 2 questions for you.", "(%d+) (%a+)"))
2, "questions"

字符串大小写转换

以下实例演示了如何对字符串大小写进行转换:

案例:

string1 = "Lua";
print(string.upper(string1))
print(string.lower(string1))

--以上代码执行结果为:
--LUA
--lua

字符串查找与反转

以下实例演示了如何对字符串进行查找与反转操作:

案例:

string = "Lua Tutorial"
-- 查找字符串
print(string.find(string,"Tutorial"))
reversedString = string.reverse(string)
print("新字符串为",reversedString)

--以上代码执行结果为:
--5    12
--新字符串为    lairotuT auL

字符串格式化

Lua 提供了 string.format() 函数来生成具有特定格式的字符串, 函数的第一个参数是格式 ,之后是对应格式中每个代号的各种数据。
由于格式字符串的存在, 使得产生的长字符串可读性大大提高了。这个函数的格式很像 C 语言中的 printf()。
以下实例演示了如何对字符串进行格式化操作:

格式字符串可能包含以下的转义码:

  • %c - 接受一个数字, 并将其转化为ASCII码表中对应的字符
  • %d, %i - 接受一个数字并将其转化为有符号的整数格式
  • %o - 接受一个数字并将其转化为八进制数格式
  • %u - 接受一个数字并将其转化为无符号整数格式
  • %x - 接受一个数字并将其转化为十六进制数格式, 使用小写字母
  • %X - 接受一个数字并将其转化为十六进制数格式, 使用大写字母
  • %e - 接受一个数字并将其转化为科学记数法格式, 使用小写字母e
  • %E - 接受一个数字并将其转化为科学记数法格式, 使用大写字母E
  • %f - 接受一个数字并将其转化为浮点数格式
  • %g(%G) - 接受一个数字并将其转化为%e(%E, 对应%G)及%f中较短的一种格式
  • %q - 接受一个字符串并将其转化为可安全被Lua编译器读入的格式
  • %s - 接受一个字符串并按照给定的参数格式化该字符串

为进一步细化格式, 可以在%号后添加参数. 参数将以如下的顺序读入:

  • (1) 符号: 一个+号表示其后的数字转义符将让正数显示正号. 默认情况下只有负数显示符号.
  • (2) 占位符: 一个0, 在后面指定了字串宽度时占位用. 不填时的默认占位符是空格.
  • (3) 对齐标识: 在指定了字串宽度时, 默认为右对齐, 增加-号可以改为左对齐.
  • (4) 宽度数值
  • (5) 小数位数/字串裁切: 在宽度数值后增加的小数部分n, 若后接f(浮点数转义符,如%6.3f)则设定该浮点数的小数只保留n位, 若后接s(字符串转义符, 如%5.3s)则设定该字符串只显示前n位.

案例:

string1 = "Lua"
string2 = "Tutorial"
number1 = 10
number2 = 20
-- 基本字符串格式化
print(string.format("基本格式化 %s %s",string1,string2))
-- 日期格式化
date = 2; month = 1; year = 2020
print(string.format("日期格式化 %02d/%02d/%03d", date, month, year))
-- 十进制格式化
print(string.format("%.4f",1/3))

--以上代码执行结果为:
--基本格式化 Lua Tutorial
--日期格式化 02/01/2020
--0.3333

其他案例:

string.format("%c", 83)                 -- 输出S
string.format("%+d", 17.0)              -- 输出+17
string.format("%05d", 17)               -- 输出00017
string.format("%o", 17)                 -- 输出21
string.format("%u", 3.14)               -- 输出3
string.format("%x", 13)                 -- 输出d
string.format("%X", 13)                 -- 输出D
string.format("%e", 1000)               -- 输出1.000000e+03
string.format("%E", 1000)               -- 输出1.000000E+03
string.format("%6.3f", 13)              -- 输出13.000
string.format("%q", "One\nTwo")         -- 输出"One\
                                        --   Two"
string.format("%s", "monkey")           -- 输出monkey
string.format("%10s", "monkey")         -- 输出    monkey
string.format("%5.3s", "monkey")        -- 输出  mon

字符与整数相互转换

案例:

-- 字符转换
-- 转换第一个字符
print(string.byte("Lua"))
-- 转换第三个字符
print(string.byte("Lua",3))
-- 转换末尾第一个字符
print(string.byte("Lua",-1))
-- 第二个字符
print(string.byte("Lua",2))
-- 转换末尾第二个字符
print(string.byte("Lua",-2))

-- 整数 ASCII 码转换为字符
print(string.char(97))

--以上代码执行结果为:
--76
--97
--97
--117
--117
--a

其他常用函数

以下实例演示了其他字符串操作,如计算字符串长度,字符串连接,字符串复制等:
string1 = "www."
string2 = "mini1"
string3 = ".cn"
-- 使用 .. 进行字符串连接
print("连接字符串",string1..string2..string3)

-- 字符串长度
print("字符串长度 ",string.len(string2))

-- 字符串复制 2 次
repeatedString = string.rep(string2,2)
print(repeatedString)

--以上代码执行结果为:
--连接字符串    www.mini1.cn
--字符串长度     6
--mini1mini1

匹配模式

Lua 中的匹配模式直接用常规的字符串来描述。它用于模式匹配函数 string.find, string.gmatch, string.gsub, string.match。你还可以在模式串中使用字符类。
字符类指可以匹配一个特定字符集合内任何字符的模式项。比如,字符类 %d 匹配任意数字。所以你可以使用模式串 %d%d/%d%d/%d%d%d%d 搜索 dd/mm/yyyy 格式的日期:

s = "Deadline is 30/05/1999, firm"
date = "%d%d/%d%d/%d%d%d%d"
print(string.sub(s, string.find(s, date)))    --> 30/05/1999

下面的表列出了Lua支持的所有字符类:
单个字符(除 ^$()%.[]*+-? 外): 与该字符自身配对

  • .(点): 与任何字符配对
  • %a: 与任何字母配对
  • %c: 与任何控制符配对(例如\n)
  • %d: 与任何数字配对
  • %l: 与任何小写字母配对
  • %p: 与任何标点(punctuation)配对
  • %s: 与空白字符配对
  • %u: 与任何大写字母配对
  • %w: 与任何字母/数字配对
  • %x: 与任何十六进制数配对
  • %z: 与任何代表0的字符配对
  • %x(此处x是非字母非数字字符): 与字符x配对. 主要用来处理表达式中有功能的字符(^$()%.[]*+-?)的配对问题, 例如%%与%配对
  • [数个字符类]: 与任何[]中包含的字符类配对. 例如[%w_]与任何字母/数字, 或下划线符号(_)配对
  • [^数个字符类]: 与任何不包含在[]中的字符类配对. 例如[^%s]与任何非空白字符配对

当上述的字符类用大写书写时, 表示与非此字符类的任何字符配对. 例如, %S表示与任何非空白字符配对.例如,'%A'非字母的字符:

> print(string.gsub("hello, up-down!", "%A", "."))
hello..up.down.    4

数字4不是字符串结果的一部分,他是gsub返回的第二个结果,代表发生替换的次数。
在模式匹配中有一些特殊字符,他们有特殊的意义,Lua中的特殊字符如下:

( ) . % + - * ? [ ^ $

'%' 用作特殊字符的转义字符,因此 '%.' 匹配点;'%%' 匹配字符 '%'。转义字符 '%'不仅可以用来转义特殊字符,还可以用于所有的非字母的字符。

模式条目可以是:

  • 单个字符类匹配该类别中任意单个字符;
  • 单个字符类跟一个 '*', 将匹配零或多个该类的字符。 这个条目总是匹配尽可能长的串;
  • 单个字符类跟一个 '+', 将匹配一或更多个该类的字符。 这个条目总是匹配尽可能长的串;
  • 单个字符类跟一个 '-', 将匹配零或更多个该类的字符。 和 '*' 不同, 这个条目总是匹配尽可能短的串;
  • 单个字符类跟一个 '?', 将匹配零或一个该类的字符。 只要有可能,它会匹配一个;
  • %n, 这里的 n 可以从 1 到 9; 这个条目匹配一个等于 n 号捕获物(后面有描述)的子串。
  • %bxy, 这里的 x 和 y 是两个明确的字符; 这个条目匹配以 x 开始 y 结束, 且其中 x 和 y 保持 平衡 的字符串。 意思是,如果从左到右读这个字符串,对每次读到一个 x 就 +1 ,读到一个 y 就 -1, 最终结束处的那个 y 是第一个记数到 0 的 y。 举个例子,条目 %b() 可以匹配到括号平衡的表达式。
  • %f[set], 指 边境模式; 这个条目会匹配到一个位于 set 内某个字符之前的一个空串, 且这个位置的前一个字符不属于 set 。 集合 set 的含义如前面所述。 匹配出的那个空串之开始和结束点的计算就看成该处有个字符 '\0' 一样。

案例:

将阿拉伯数字转换为语文汉字数字: ``` lua local function NumToCN(num) local size = #tostring(num) local CN = "" local StrCN = {"一","二","三","四","五","六","七","八","九"} for i = 1 , size do CN = CN .. StrCN[tonumber(string.sub(tostring(num), i , i))] end return CN end print(NumToCN(56665)) ```

# Lua数组

概述:

数组,就是相同数据类型的元素按一定顺序排列的集合,可以是一维数组和多维数组。 Lua 数组的索引键值可以使用整数表示,数组的大小不是固定的。

一维数组:

一维数组是最简单的数组,其逻辑结构是线性表。一维数组可以用for循环出数组中的元素,如下实例:
array = {"Lua", "Tutorial"}

for i= 0, 2 do
   print(array[i])
end

--以上代码执行输出结果为:
--nil
--Lua
--Tutorial

正如你所看到的,我们可以使用整数索引来访问数组元素,如果知道的索引没有值则返回nil。 在 Lua 索引值是以 1 为起始,但你也可以指定 0 开始。
除此外我们还可以以负数为数组索引值:

array = {}

for i= -2, 2 do
   array[i] = i *2
end

for i = -2,2 do
   print(array[i])
end

--以上代码执行输出结果为:
-- -4
-- -2
-- 0
-- 2
-- 4

多维数组:

多维数组即数组中包含数组或一维数组的索引键对应一个数组。
以下是一个三行三列的阵列多维数组:

-- 初始化数组
array = {}
for i=1,3 do
   array[i] = {}
      for j=1,3 do
         array[i][j] = i*j
      end
end
-- 访问数组
for i=1,3 do
   for j=1,3 do
      print(array[i][j])
   end
end
--以上代码执行输出结果为:
--1
--2
--3
--2
--4
--6
--3
--6
--9

不同索引键的三行三列阵列多维数组:

-- 初始化数组
array = {}
maxRows = 3
maxColumns = 3
for row=1,maxRows do
   for col=1,maxColumns do
      array[row*maxColumns +col] = row*col
   end
end

-- 访问数组
for row=1,maxRows do
   for col=1,maxColumns do
      print(array[row*maxColumns +col])
   end
end
--以上代码执行输出结果为:
--1
--2
--3
--2
--4
--6
--3
--6
--9

# Lua table(表)

概述:

table 是 Lua 的一种数据结构用来帮助我们创建不同的数据类型,如:数组、字典等。 Lua table 使用关联型数组,你可以用任意类型的值来作数组的索引,但这个值不能是 nil。 Lua table 是不固定大小的,你可以根据自己需要进行扩容。 Lua也是通过table来解决模块(module)、包(package)和对象(Object)的。 例如string.format表示使用"format"来索引table string。

table(表)的构造:

构造器是创建和初始化表的表达式。表是Lua特有的功能强大的东西。最简单的构造函数是{},用来创建一个空表。可以直接初始化数组:

-- 初始化表
mytable = {}

-- 指定值
mytable[1]= "Lua"

-- 移除引用
mytable = nil
-- lua 垃圾回收会释放内存

当我们为 table a 并设置元素,然后将 a 赋值给 b,则 a 与 b 都指向同一个内存。如果 a 设置为 nil ,则 b 同样能访问 table 的元素。如果没有指定的变量指向a,Lua的垃圾回收机制会清理相对应的内存。 以下实例演示了以上的描述情况:

-- 简单的 table
mytable = {}
print("mytable 的类型是 ",type(mytable))

mytable[1]= "Lua"
mytable["wow"] = "修改前"
print("mytable 索引为 1 的元素是 ", mytable[1])
print("mytable 索引为 wow 的元素是 ", mytable["wow"])

-- alternatetable和mytable的是指同一个 table
alternatetable = mytable

print("alternatetable 索引为 1 的元素是 ", alternatetable[1])
print("mytable 索引为 wow 的元素是 ", alternatetable["wow"])

alternatetable["wow"] = "修改后"

print("mytable 索引为 wow 的元素是 ", mytable["wow"])

-- 释放变量
alternatetable = nil
print("alternatetable 是 ", alternatetable)

-- mytable 仍然可以访问
print("mytable 索引为 wow 的元素是 ", mytable["wow"])

mytable = nil
print("mytable 是 ", mytable)

以上代码执行结果为:

mytable 的类型是     table
mytable 索引为 1 的元素是     Lua
mytable 索引为 wow 的元素是     修改前
alternatetable 索引为 1 的元素是     Lua
mytable 索引为 wow 的元素是     修改前
mytable 索引为 wow 的元素是     修改后
alternatetable 是     nil
mytable 索引为 wow 的元素是     修改后
mytable 是     nil

Table操作:

序号 方法 & 用途
1 table.concat (table [, sep [, start [, end]]]):
concat是concatenate(连锁, 连接)的缩写. table.concat()函数列出参数中指定table的数组部分从start位置到end位置的所有元素, 元素间以指定的分隔符(sep)隔开。
2 table.insert (table, [pos,] value):
在table的数组部分指定位置(pos)插入值为value的一个元素. pos参数可选, 默认为数组部分末尾.
3 table.maxn (table)
指定table中所有正数key值中最大的key值. 如果不存在key值为正数的元素, 则返回0。(Lua5.2之后该方法已经不存在了,本文使用了自定义函数实现)
4 table.remove (table [, pos])
返回table数组部分位于pos位置的元素. 其后的元素会被前移. pos参数可选, 默认为table长度, 即从最后一个元素删起。
5 table.sort (table [, comp])
对给定的table进行升序排序。

接下来我们来看下这几个方法的实例。

Table连接:

我们可以使用 concat() 输出一个列表中元素连接成的字符串:

fruits = {"banana","orange","apple"}
-- 返回 table 连接后的字符串
print("连接后的字符串 ",table.concat(fruits))

-- 指定连接字符
print("连接后的字符串 ",table.concat(fruits,", "))

-- 指定索引来连接 table
print("连接后的字符串 ",table.concat(fruits,", ", 2,3))

--执行以上代码输出结果为:
--连接后的字符串     bananaorangeapple
--连接后的字符串     banana, orange, apple
--连接后的字符串     orange, apple

插入和移除:

以下实例演示了 table 的插入和移除操作:

fruits = {"banana","orange","apple"}

-- 在末尾插入
table.insert(fruits,"mango")
print("索引为 4 的元素为 ",fruits[4])

-- 在索引为 2 的键处插入
table.insert(fruits,2,"grapes")
print("索引为 2 的元素为 ",fruits[2])

print("最后一个元素为 ",fruits[5])
table.remove(fruits)
print("移除后最后一个元素为 ",fruits[5])


--执行以上代码输出结果为:
--索引为 4 的元素为     mango
--索引为 2 的元素为     grapes
--最后一个元素为     mango
--移除后最后一个元素为     nil

Table 排序:

以下实例演示了 sort() 方法的使用,用于对 Table 进行排序:

fruits = {"banana","orange","apple","grapes"}
print("排序前")
for k,v in ipairs(fruits) do
        print(k,v)
end

table.sort(fruits)
print("排序后")
for k,v in ipairs(fruits) do
        print(k,v)
end

--执行以上代码输出结果为:
--排序前
--1    banana
--2    orange
--3    apple
--4    grapes
--排序后
--1    apple
--2    banana
--3    grapes
--4    orange

Table 最大值:

table.maxn 在 Lua5.2 之后该方法已经不存在了,我们定义了 table_maxn 方法来实现。 以下实例演示了如何获取 table 中的最大值:

function table_maxn(t)
  local mn=nil;
  for k, v in pairs(t) do
    if(mn==nil) then
      mn=v
    end
    if mn < v then
      mn = v
    end
  end
  return mn
end
tbl = {[1] = 2, [2] = 6, [3] = 34, [26] =5}
print("tbl 最大值:", table_maxn(tbl))
print("tbl 长度 ", #tbl)


--执行以上代码输出结果为:
--tbl 最大值:    34
--tbl 长度     3

注意:

当我们获取 table 的长度的时候无论是使用 # 还是 table.getn 其都会在索引中断的地方停止计数,而导致无法正确取得 table 的长度。 可以使用以下方法来代替:

function table_leng(t)
  local leng=0
  for k, v in pairs(t) do
    leng=leng+1
  end
  return leng;
end
上次更新: 2021/8/30 18:06:36