Lua基础教学

x33g5p2x  于2021-09-25 转载在 其他  
字(34.5k)|赞(0)|评价(0)|浏览(499)

Lua基础教学

Lua 特性

  • 轻量级: 它用标准C语言编写并以源代码形式开放,编译后仅仅一百余K,可以很方便的嵌入别的程序里。

  • 可扩展: Lua提供了非常易于使用的扩展接口和机制:由宿主语言(通常是C或C++)提供这些功能,Lua可以使用它们,就像是本来就内置的功能一样。

  • 其它特性:

  • 支持面向过程(procedure-oriented)编程和函数式编程(functional programming);

  • 自动内存管理;只提供了一种通用类型的表(table),用它可以实现数组,哈希表,集合,对象;

  • 语言内置模式匹配;闭包(closure);函数也可以看做一个值;提供多线程(协同进程,并非操作系统所支持的线程)支持;

  • 通过闭包和table可以很方便地支持面向对象编程所需要的一些关键机制,比如数据抽象,虚函数,继承和重载等。

Lua 应用场景

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

Lua 环境安装

这里就不说怎么安装了 基本上Centos7以上都自带 直接输入 lua 就可以发现 出现 版本 5.1.0 以上的lua

使用 which lua lua 查看 lua 的编译路径 我的是 /usr/bin/lua

lua 5.1 肯定不行的 很多语法不支持 我们要 5.2 以上的

检测 gcc编译器是否存在

gcc -v

没有存在那么 安装gcc

yum -y install gcc gcc-c++ kernel-devel   #安装gcc、c++编译器以及内核文件

然后我们安装lua 4

先按照 lua需要的依赖

sudo yum install -y readline-devel

http://www.lua.org/ 这个是官网 可以去找最新版的

curl -R -O http://www.lua.org/ftp/lua-5.4.2.tar.gz

tar zxf lua-5.4.2.tar.gz

cd lua-5.4.2

make all test

make install

设置 lua 的 环境变量 PATH

PATH=/usr/local/bin:$PATH

. ~/.bash_profile

然后 在查看 lua 信息

lua -v
Lua 5.4.2  Copyright (C) 1994-2020 Lua.org, PUC-Rio

然后在查看lua的编译路径 which lua lua 我的是 /usr/local/bin/lua 记住这个路径 我们下面会用到

Lua 语法

lua有交互式编程和脚本式编程。

交互式编程就是在命令行中输入语法并立即查看效果。

脚本式编程需要编写脚本,然后再执行命令 执行脚本才可以。

一般采用脚本式编程。(例如:编写一个hello.lua的文件,输入文件内容,并执行lua hell.lua即可)

Lua 交互式编程模式可以通过命令 lua -i 或 lua 来启用:

我们来完成我们lua的一个入门程序

在交互模式编程

print("Hello World!")

Hello World!

后面的语法教程中就不在贴图了 看代码就行了

ctrl+c 结束交互编程

脚本式编程

我们可以将 Lua 程序代码保存到一个以 lua 结尾的文件,并执行,该模式称为脚本式编程,

如我们将如下代码存储在名为 hello.lua 的脚本文件中:

vi hello.lua

文件内容:

print("Hello World!")
print("m.runoob.com")

使用 lua hello.lua 执行以上脚本,输出结果为:

Hello World!
m.runoob.com

我们也可以将代码修改为如下形式来执行脚本(在文件内容开头添加:/#! /usr/bin/lua ):

这里注意 /usr/bin/lua 就是我们在上面查询出来的 你本地lua的位置

如下:

#! /usr/local/bin/lua

print("Hello World!")
print("m.runoob.com")

以上代码中,我们指定了 当前文本的 Lua 的解释器 /#! /usr/local/bin/lua

接下来我们为脚本添加可执行权限,并执行:

chmod 777  hello.lua    # 最大权限 或者 chmod +x hello.lu 执行权限

./hello.lua   #运行脚本文件

Hello World!
m.runoob.com

注释

单行注释

两个减号是单行注释:

-- 我是单行注释

多行注释

--[[ 多行注释 多行注释 --]]

标示符

Lua 标示符用于定义一个变量,函数获取其他用户定义的项。标示符以一个字母 A 到 Z 或 a 到 z 或下划线 _ 开头后加上 0 个或多个字母,下划线,数字(0 到 9)。

最好不要使用下划线加大写字母的标示符,因为Lua的保留字也是这样的。

Lua 不允许使用特殊字符如 @, $, 和 % 来定义标示符。 Lua 是一个区分大小写的编程语言。因此在 Lua 中 A 与 a 是两个不同的字符。

以下列出了一些正确的标示符:

mohd         zara      abc     move_name    a_123
myname50     _temp     j       a23b9        retVal

关键词

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

关键字关键字关键字关键字
andbreakdoelse
elseifendfalsefor
functionifinlocal
nilnotorrepeat
returnthentrueuntil
whilegoto

变量

变量在使用前,必须在代码中进行声明,即创建该变量。

编译程序执行代码之前编译器需要知道如何给语句变量开辟存储区,用于存储变量的值。

Lua 变量有三种类型:全局变量、局部变量、表中的域。

Lua 中的变量全是全局变量,那怕是语句块或是函数里,除非用 local 显式声明为局部变量,也就是 在默认情况下变量总是认为是全局的。

局部变量的作用域为从声明位置开始到所在语句块结束。

变量的默认值均为 nil。

列:

创建 test.lua脚本

vi test.lua

文件内容

a = 5               -- 全局变量
local b = 5         -- 局部变量

function joke()
    c = 5           -- 全局变量
    local d = 6     -- 局部变量
end

joke()
print(c,d)          --> 5 nil

do 
    local a = 6     -- 局部变量
    b = 6           -- 全局变量
    print(a,b);     --> 6 6
end

print(a,b)      --> 5 6

运行脚本

lua test.lua

5 nil
6 6
5 6

变量不需要声明类型 会自动适配类型 ,给一个变量赋值后即创建了这个全局变量,访问一个没有初始化的全局变量也不会出错,只不过得到的结果是:nil。

直接输入b变量 这个b我们之前并没有声明过

print(b)

nil

b=10
print(b)

10

如果你想删除一个全局变量,只需要将变量赋值为nil。

b = nil
print(b)

nil

赋值语句

Lua可以对多个变量同时赋值,变量列表和值列表的各个元素用逗号分开,赋值语句右边的值会依次赋给左边的变量。

a, b = 10, 2*3        --结果: a=10; b=6

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

a. 变量个数 > 值的个数 按变量个数补足nil

b. 变量个数 < 值的个数 多余的值会被忽略

例如

a, b, c = 0, 1
print(a,b,c)             --> 0 1 nil
 
a, b = 1, 2, 2     
print(a,b)               --> 1 2
 
a, b, c = 0
print(a,b,c)             --> 0 nil nil

在实际中 如果要对多个变量赋值必须依次对每个变量赋值。 (避免后期出问题)

a, b, c = 0, 0, 0
print(a,b,c)             --> 0 0 0

多值赋值经常用来交换变量,或将函数调用返回给变量: 比如我有一个函数 f( ) 返回了两个值

a, b = f()

f()返回两个值,第一个赋给a,第二个赋给b。

应该尽可能的使用局部变量,有两个好处:

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

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。
~=不等于,检测两个值是否相等,不相等返回 true,否则返回 false(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

逻辑运算符

下表列出了 Lua 语言中的常用逻辑运算符,设定 A 的值为 true,B 的值为 false:

操作符描述实例
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 = "World"

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

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

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

连接字符串 a 和 b Hello World
b 字符串长度 5
字符串 Test 长度 4

运算符优先级

从高到低的顺序:

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

除了 ^ 外所有的二元运算符都是左连接的。 也就表达式 是从左向右开始 依次计算

我们可以通过以下实例来更加透彻的了解 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 数据类型

Lua 是动态类型语言,变量不要类型定义,只需要为变量赋值。 值可以存储在变量中,作为参数传递或结果返回。

Lua 中有 8 个基本类型分别为:nil、boolean、number、string、userdata、function、thread 和 table。

数据类型描述
nil这个最简单,只有值nil属于该类,表示一个无效值(在条件表达式中相当于false)。
boolean包含两个值:false和true。
number表示双精度类型的实浮点数
string字符串由一对双引号或单引号来表示
function由 C 或 Lua 编写的函数
userdata表示任意存储在变量中的C数据结构
thread表示执行的独立线路,用于执行协同程序
tableLua 中的表(table)其实是一个"关联数组"(associative arrays),数组的索引可以是数字、字符串或表类型。在 Lua 里,table 的创建是通过"构造表达式"来完成,最简单构造表达式是{},用来创建一个空表。

我们可以使用 type 函数测试给定变量或者值的类型:

print(type("Hello world"))      --> string
print(type(10.4*3))             --> number
print(type(print))              --> function
print(type(type))               --> function
print(type(true))               --> boolean
print(type(nil))                --> nil
print(type(type(X)))            --> string
print(type({}))  				--> table
nil(空)

nil 类型表示一种没有任何有效值,它只有一个值 – nil,例如打印一个没有赋值的变量,便会输出一个 nil 值:

print(type(a))

nil

对于全局变量和 table,nil 还有一个"删除"作用,给全局变量或者 table 表里的变量赋一个 nil 值,等同于把它们删掉:

tab1 = { key1 = "val1", key2 = "val2", "val3" }

for k, v in pairs(tab1) do
    print(k .. " - " .. v)
end

key1 - val1
key2 - val2

tab1.key1 = nil
for k, v in pairs(tab1) do
    print(k .. " - " .. v)
end

key2 - val2

nil 作比较时应该加上双引号 ""

print(type(X) == nil)

false

print(type(X)=="nil")

true

type(X)==nil 结果为 false 的原因是 type(X) 实质是返回的 “nil” 字符串,是一个 string 类型:

boolean(布尔)

boolean 类型只有两个可选值:true(真) 和 false(假),Lua 把 false 和 nil 看作是 false,其他的都为 true,数字 0 也是 true:

if false or nil then
    print("至少有一个是 true")
else
    print("false 和 nil 都为 false")
end

false 和 nil 都为 false

if 0 then
    print("数字 0 是 true")
else
    print("数字 0 为 false")
end

数字 0 是 true

number(数字)

Lua 默认只有一种 number 类型 – double(双精度)类型(默认类型可以修改 luaconf.h 里的定义),

以下几种写法都被看作是 number 类型:

print(type(2))
print(type(2.2))
print(type(0.2))
print(type(2e+1))
print(type(0.2e-1))
print(type(7.8263692594256e-06))

number

string(字符串)

字符串或串(String)是由数字、字母、下划线组成的一串字符。

Lua 语言中字符串可以使用以下三种方式来表示:

  • 单引号间的一串字符。
  • 双引号间的一串字符。
  • ** 与 ** 间的一串字符。

以上三种方式的字符串实例如下:

string1 = "Lua"
print("\"字符串 1 是\"",string1)
string2 = 'runoob.com'
print("字符串 2 是",string2)

string3 = [["Lua 教程"]]
print("字符串 3 是",string3)

“字符串 1 是” Lua
字符串 2 是 runoob.com
字符串 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
\ddd1到3位八进制数所代表的任意字符三位八进制
\xhh1到2位十六进制所代表的任意字符二位十六进制

字符串操作

Lua 提供了很多的方法来支持字符串的操作:

string.upper(argument): 字符串全部转为大写字母。

str="abc"
str=string.upper(str)
print(str)

ABC
*
string.lower(argument): 字符串全部转为小写字母。

str="ABc"
str=string.lower(str)
print(str)

abc
*
string.gsub(mainString,findString,replaceString,num) 字符串替换

mainString 为要操作的字符串,

findString 为被替换的字符,

replaceString 要替换的字符,

num 替换次数(可以忽略,则全部替换)

a=string.gsub("aaaa","a","z",3);
 print(a)

zzza
*
string.find (str, substr, [init, [end]])

在一个指定的目标字符串中搜索指定的内容(第三个参数为索引 代表从什么位置开始查询 加快查询速度)

结果返回查询到的其具体位置。不存在则返回 nil。

下标从 1开始 而不是0

index=string.find("Hello Lua user", "Lua", 2) 
print(index)

7
*
string.reverse(arg) 字符串反转

str=string.reverse("Lua")
print(str)

auL
*
string.format(…) 返回一个类似printf的格式化字符串

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 = 2014
print(string.format("日期格式化 %02d/%02d/%03d", date, month, year))
-- 十进制格式化
print(string.format("%.4f",1/3))  --保留4位小数

基本格式化 Lua Tutorial
日期格式化 02/01/2014
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

string.char(arg) 和 string.byte(arg[,int])

char 将整型数字转成字符并连接, byte 转换字符为整数值(可以指定某个字符,默认第一个字符)。

str=string.char(97,98,99,100)
print(str)

abcd

str=string.byte("ABCD",4)
print(str)

68
*
string.len(arg) 计算字符串长度

str=string.len("abc")
print(str)

3
*
string.rep(string, n) 返回字符串string的n个拷贝

str=string.rep("abcd",2)
print(str)

abcdabcd
*
链接两个字符串

print("www.runoob.".."com")

www.runoob.com
*
string.gmatch(str, pattern)

回一个迭代器函数,每一次调用这个函数,返回一个在字符串 str 找到的下一个符合 pattern 描述的子串。如果参数 pattern 描述的字符串没有找到,迭代函数返回nil。

for word in string.gmatch("Hello Lua user", "%a+") do 
    print(word) 
end

Hello
Lua
user
*
//string.match(str, pattern, init) // 查询字符串中否包含某字符

string.match()只寻找源字串str中的第一个配对. 参数init可选, 指定搜寻过程的起点, 默认为1。
在成功配对时, 函数将返回配对表达式中的所有捕获结果;

如果没有设置捕获标记, 则返回整个配对字符串.

当没有成功的配对时, 返回nil

str=string.match("I have 2 questions for you.", "have")
print(str)

have

还可以这样

str1=string.match("I have 2 questions for you.", "%d+ %a+")
print(str)

2 questions
*
字符串截取

字符串截取使用 sub() 方法。

string.sub() 用于截取字符串,原型为:

string.sub(s, i [j1, j2])

参数说明:

  • s:要截取的字符串。
  • i: 截取开始位置。
  • j1:截取开始位置,默认为 1,开始第一个字符。
  • j2: 截取结束位置,默认为 -1,最后一个字符。 (可以省略)
-- 截取第4个字符 到 最后一个
first_sub = string.sub("asdfghjk", 4)
print("\n截取结果", first_sub)   -- fghjk

-- 截取第1个 到第3个 字符 
first_sub = string.sub("asdfghjk", 1, 3)
print("\n截取结果", first_sub)   -- asd

-- 截取最后3个
third_sub = string.sub("asdfghjk", -3)
print("\n截取结果", third_sub)    -- hjk

-- 截取 字符串第一个位置 到 字符串倒数第3个 位置
-- 索引越界,输出原始字符串
fourth_sub = string.sub("asdfghjk", 1,-3)
print("\n截取结果",  fourth_sub)  --asdfgh

-- 字符串截取 越界 将截 从开始 到 剩下的全部字符串
fourth_sub = string.sub("asdfghjk", 3,111)
print("\n截取结果",  fourth_sub)  --asdfgh

字符与整数相互转换

-- 字符转换
-- 转换第一个字符
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

在对一个数字字符串上进行算术操作时,Lua 会尝试将这个数字字符串转成一个数字:

print("2" + 6)

8.0

print("2" + "6")

8.0

print("2 + 6")

2 + 6

print("-2e2" * "6")

-1200.0

print("error" + 1)

报错 非数值的字符不能和数字相加

上代码中"error" + 1执行报错了,字符串连接使用的是 … ,如:

print("a" .. 'b')

ab

print(157 .. 428)

157428

匹配模式

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 号捕获物(后面有描述)的子串。
  • %b/*xy/*, 这里的 xy 是两个明确的字符; 这个条目匹配以 x 开始 y 结束, 且其中 xy 保持 平衡 的字符串。 意思是,如果从左到右读这个字符串,对每次读到一个 x+1 ,读到一个 y-1, 最终结束处的那个 y 是第一个记数到 0 的 y。 举个例子,条目 %b() 可以匹配到括号平衡的表达式。
  • %f[/*set/*], 指 边境模式; 这个条目会匹配到一个位于 set 内某个字符之前的一个空串, 且这个位置的前一个字符不属于 set 。 集合 set 的含义如前面所述。 匹配出的那个空串之开始和结束点的计算就看成该处有个字符 ‘\0’ 一样。

模式:

模式 指一个模式条目的序列。 在模式最前面加上符号 ‘^’ 将锚定从字符串的开始处做匹配。 在模式最后面加上符号 ‘$’ 将使匹配过程锚定到字符串的结尾。 如果 ‘^’ 和 ‘$’ 出现在其它位置,它们均没有特殊含义,只表示自身。

捕获:

模式可以在内部用小括号括起一个子模式; 这些子模式被称为 捕获物。 当匹配成功时,由 捕获物 匹配到的字符串中的子串被保存起来用于未来的用途。 捕获物以它们左括号的次序来编号。 例如,对于模式 "(a/*(.)%w(%s/*))" , 字符串中匹配到 "a/*(.)%w(%s/*)" 的部分保存在第一个捕获物中 (因此是编号 1 ); 由 “.” 匹配到的字符是 2 号捕获物, 匹配到 “%s/*” 的那部分是 3 号。

作为一个特例,空的捕获 () 将捕获到当前字符串的位置(它是一个数字)。 例如,如果将模式 "()aa()" 作用到字符串 "flaaap" 上,将产生两个捕获物: 3 和 5 。

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={{1,2,3},{4,5,6},{7,8,9}}
-- 访问数组
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

正如你所看到的,以上的实例中,数组设定了指定的索引值,这样可以避免出现 nil 值,有利于节省内存空间。

table(表)

table 是 Lua 的一种数据结构用来帮助我们创建不同的数据类型,如:数组、字典等。

Lua table 使用关联型数组,你可以用任意类型的值来作数组的索引,但这个值不能是 nil。

Lua table 是不固定大小的,你可以根据自己需要进行扩容。

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

-- 初始化表
mytable = {}

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

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

--定义时候初始化
mytable={a=1,b=2}

-- 混合
mytable={a="axx",b="bxxx",1,2,3,4}

-- 方法
smartMan = {
        name = "none",
        sayHello = function()
            print("大家好,我是聪明的豪。");
        end
    }

数组 通过[ index] 来调用 没有变化

字典可以通过 mytable.key 或者 mytable[key] 来调用

对字典内的 方法调用 smartMan.sayHello() 要加 ()

当我们为 table 的a 设置元素,然后将 a 赋值给 b,则 a 与 b 都指向同一个内存。

如果 a 设置为 nil ,则 b 同为null

tab = {}
tab["a"]=nil
b=tab["a"]
print(b)  ---nil
-- 简单的 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("alternatetable 索引为 wow 的元素是 ", alternatetable["wow"])

alternatetable["wow"] = "修改后"
print("mytable 索引为 wow 的元素是 ", mytable["wow"])  --修改后

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

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

Table 操作

以下列出了 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)//*对给定的table进行升序排序。
6自定义获取长度的函数 当然 table.getn(table) 和 /# 也能获取长度 但是会遇到nil出现bug

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,5)
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 其都会在索引 中断的地方停止计数比如遇到nil

而导致无法正确取得 table 的长度。

可以使用以下方法来代替:

function table_length(t)
  local leng=0
  for k, v in pairs(t) do
    leng=leng+1
  end
  return leng;
end

array={1,nil,2,3,nil,4,5}

print(table_length(array))   --5
function(函数)

在 Lua 中,函数是被看作是 " 第一类值(First-Class Value)",函数可以存在变量里:

创建 function_test.lua脚本文件

vi function_test.lua

文件内容

function factorial1(n)    
    if n == 0 then
        return 1
    else
        return n * factorial1(n - 1)      --递归 (函数自己调用自己) 5*4*3*2*1=120
    end
    
end    --函数结尾

print(factorial1(5))    --给函数赋值
factorial2 = factorial1   --建函数 factorial1 复制一份给 factorial2
print(factorial2(5))   --操作factorial2 函数

执行脚本

lua function_test.lua

120
120

function 可以以匿名函数的方式通过参数传递: 这种方式理解有点困难

修改 function_test.lua脚本文件

vi function_test.lua

内容

function testFun(tab,fun)
        for k ,v in pairs(tab) do
                print(fun(k,v));  --内部调用匿名函数
        end
end

tab={key1="val1",key2="val2"};  --定义表

--调用testFun函数 向此函数的 参数1传入表tab 参数2 传入匿名函数
testFun(tab,
    function(key,val)--匿名函数
        return key.."="..val;
	end
);

执行脚本

vi function_test.lua

key1=val1
key2=val2

thread(线程)

在 Lua 里,最主要的线程是协同程序(coroutine)。它跟线程(thread)差不多,拥有自己独立的栈、局部变量和指令指针,可以跟其他协同程序共享全局变量和其他大部分东西。

线程跟协程的区别:线程可以同时多个运行,而协程任意时刻只能运行一个,并且处于运行状态的协程只有被挂起(suspend)时才会暂停。

和java多线程差不多 但是如果你不是 做运维的这个了解下就行了 没必要 学习 基本不用

如果想要深入了解 这里我找到了二篇不错的博客

https://www.cnblogs.com/Richard-Core/p/4373582.html

CSDN-浴霸x子

userdata(自定义类型)

userdata 是一种用户自定义数据,用于表示一种由应用程序或 C/C++ 语言库所创建的类型,可以将任意 C/C++ 的任意数据类型的数据(通常是 struct 和 指针)存储到 Lua 变量中调用。

其实就是使用c或c++写的代码然后可以在lua中使用

但是如果你不是 做运维的这个了解下就行了 参考博客

https://www.cnblogs.com/herenzhiming/articles/6101767.html

https://www.cnblogs.com/sifenkesi/p/3897245.html

Lua 流程控制

Lua 编程语言流程控制语句通过程序设定一个或多个条件语句来设定。在条件为 true 时执行指定程序代码,在条件为 false 时执行其他指定代码。

以下是典型的流程控制流程图:

控制结构的条件表达式结果可以是任何值,Lua认为false和nil为假,true和非nil为真。

要注意的是Lua中 0 为 true:

--[ 0 为 true ]
if(0)
then
    print("0 为 true")
end

以上代码输出结果为:

0 为 true

Lua 提供了以下控制结构语句:

Lua 提供了以下控制结构语句:

语句描述
if 语句if 语句 由一个布尔表达式作为条件判断,其后紧跟其他语句组成。
if…else 语句if 语句 可以与 else 语句搭配使用, 在 if 条件表达式为 false 时执行 else 语句代码。
if 嵌套语句你可以在ifelse if中使用一个或多个 ifelse if 语句 。
if 语句

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

Lua if 语句语法格式如下:

if (布尔表达式)
then
   --[ 在布尔表达式为 true 时执行的语句 --]
end

在布尔表达式为 true 时会if中的代码块会被执行,在布尔表达式为 false 时,紧跟在 if 语句 end 之后的代码会被执行。Lua认为false和nil为假,true 和非nil为真。要注意的是Lua中 0 为 true。if 语句流程图如下:

以下实例用于判断变量 a 的值是否小于 20:

--[ 定义变量 --]
a = 10;

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

以上代码执行结果如下:

a 小于 20

a 的值为: 10

if…else 语句

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

Lua if…else 语句语法格式如下:

if(布尔表达式)
then
   --[ 布尔表达式为 true 时执行该语句块 --]
else
   --[ 布尔表达式为 false 时执行该语句块 --]
end

在布尔表达式为 true 时会if中的代码块会被执行,在布尔表达式为 false 时,else 的代码块会被执行。

Lua认为false和nil为假,true 和非nil为真。要注意的是Lua中 0 为 true。

if 语句流程图如下:

以下实例用于判断变量 a 的值:

--[ 定义变量 --]
a = 100;
--[ 检查条件 --]
if( a < 20 )
then
   --[ if 条件为 true 时执行该语句块 --]
   print("a 小于 20" )
else
   --[ if 条件为 false 时执行该语句块 --]
   print("a 大于 20" )
end
print("a 的值为 :", a)

以上代码执行结果如下:

a 大于 20
a 的值为 : 100

if…elseif…else 语句

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

Lua if…elseif…else 语句语法格式如下:

if( 布尔表达式 1)
then
   --[ 在布尔表达式 1 为 true 时执行该语句块 --]

elseif( 布尔表达式 2)
then
   --[ 在布尔表达式 2 为 true 时执行该语句块 --]

elseif( 布尔表达式 3)
then
   --[ 在布尔表达式 3 为 true 时执行该语句块 --]
else 
   --[ 如果以上布尔表达式都不为 true 则执行该语句块 --]
end

以下实例对变量 a 的值进行判断

--[ 定义变量 --]
a = 100

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

没有匹配 a 的值
a 的真实值为: 100

if 嵌套语句

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

Lua if 嵌套语句语法格式如下:

if( 布尔表达式 1)
then
   --[ 布尔表达式 1 为 true 时执行该语句块 --]
   if(布尔表达式 2)
   then
      --[ 布尔表达式 2 为 true 时执行该语句块 --]
   end
end

你可以用同样的方式嵌套else if…else语句。

以下实例用于判断变量 a 和 b 的值:

--[ 定义变量 --]
a = 100;
b = 200;

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

a 的值为 100 b 的值为 200
a 的值为 : 100
b 的值为 : 200

Lua 循环

介绍

很多情况下我们需要做一些有规律性的重复操作,因此在程序中就需要重复执行某些语句。

一组被重复执行的语句称之为循环体,能否继续重复,决定循环的终止条件。

循环结构是在一定条件下反复执行某段程序的流程结构,被反复执行的程序被称为循环体。

循环语句是由循环体及循环的终止条件两部分组成的。

Lua 语言提供了以下几种循环处理方式:

循环类型描述
while 循环在条件为 true 时,让程序重复地执行某些语句。执行语句前会先检查条件是否为 true。
for 循环重复执行指定语句,重复次数可在 for 语句中控制。
repeat…until重复执行循环,直到 指定的条件为真时为止
循环嵌套可以在循环内嵌套一个或多个循环语句(while、for、do…while)

循环控制语句

循环控制语句用于控制程序的流程, 以实现程序的各种结构方式。

Lua 支持以下循环控制语句:

控制语句描述
break 语句退出当前循环或语句,并开始脚本执行紧接着的语句。
goto 语句将程序的控制点转移到一个标签处。

无限循环

在循环体中如果条件永远为 true 循环语句就会永远执行下去,以下以 while 循环为例:

while( true )
do
   print("循环将永远执行下去")
end
while循环

Lua 编程语言中 while 循环语句在判断条件为 true 时会重复执行循环体语句。

Lua 编程语言中 while 循环语法:

while(condition)
do
   statements
end

statements(循环体语句) 可以是一条或多条语句,condition(条件) 可以是任意表达式,在 condition(条件) 为 true 时执行循环体语句。

在以上流程图中我们可以看出在/*/condition(条件)//*为 false 时会跳过当前循环并开始脚本执行紧接着的语句。

以下实例循环输出 a 的值:

a=10
while( a < 15 )
do
   print("a 的值为:", a)
   a = a+1
end

结果:

a 的值为: 10
a 的值为: 11
a 的值为: 12
a 的值为: 13
a 的值为: 14

for 循环

Lua 编程语言中 for 循环语句可以重复执行指定语句,重复次数可在 for 语句中控制。

Lua 编程语言中 for语句有两大类::

  • 数值for循环
  • 泛型for循环

数值for循环

Lua 编程语言中数值 for 循环语法格式

for var=exp1,exp2,exp3 do  
    <执行体>  
end

var 从 exp1 变化到 exp2,每次变化以 exp3 为步长递增 var,并执行一次 “执行体”。exp3 是可选的,如果不指定,默认为1递增。

for i=1,f(x) do   -- 省略 exp3
    print(i)
end
 

for i=10,1,-1 do   --每次循环 i-1
    print(i)
end

for的三个表达式在循环开始前一次性求值,以后不再进行求值。比如上面的f(x)只会在循环开始前执行一次,其结果用在后面的循环中。

验证如下:

vi  for.lua
#!/usr/bin/lua 
function f(x)  
    print("function")  
    return x*2  
end  
for i=1,f(2) do
    print(i)  
end

启动脚本

.for.lua

function

1
2
3
4

可以看到 函数f(x)只在循环开始前执行一次。

泛型for循环

泛型 for 在自己内部保存迭代函数,实际上它保存三个值:迭代函数、状态常量、控制变量。

泛型 for 迭代器提供了集合的 key/value 对,语法格式如下:

for k, v in pairs(t) do
    print(k, v)
end

上面代码中,k, v为变量列表;pairs(t)为表达式列表。

实例

array = {"Google", "Runoob"}

for key,value in ipairs(array)
do
   print(key, value)
end

1 Google
2 Runoob

以上实例中我们使用了 Lua 默认提供的迭代函数 ipairs。

下面我们看看泛型 for 的执行过程:

  • 首先,初始化,计算 in 后面表达式的值,表达式应该返回泛型 for 需要的三个值:迭代函数、状态常量、控制变量;与多值赋值一样,如果表达式返回的结果个数不足三个会自动用 nil 补足,多出部分会被忽略。
  • 第二,将状态常量和控制变量作为参数调用迭代函数(注意:对于 for 结构来说,状态常量没有用处,仅仅在初始化时获取他的值并传递给迭代函数)。
  • 第三,将迭代函数返回的值赋给变量列表。
  • 第四,如果返回的第一个值为nil循环结束,否则执行循环体。
  • 第五,回到第二步再次调用迭代函数

在Lua中我们常常使用函数来描述迭代器,每次调用该函数就返回集合的下一个元素。Lua 的迭代器包含以下两种类型:

  • 无状态的迭代器
  • 多状态的迭代器

无状态的迭代器

无状态的迭代器是指不保留任何状态的迭代器,因此在循环中我们可以利用无状态迭代器避免创建闭包花费额外的代价。每一次迭代,迭代函数都是用两个变量(状态常量和控制变量)的值作为参数被调用,一个无状态的迭代器只利用这两个值可以获取下一个元素。

这种无状态迭代器的典型的简单的例子是 ipairs,它遍历数组的每一个元素。

以下实例我们使用了一个简单的函数来实现迭代器,实现 数字 n 的平方:

function square(iteratorMaxCount,currentNumber)
   if currentNumber<iteratorMaxCount
   then
      currentNumber = currentNumber+1
   return currentNumber, currentNumber*currentNumber
   end
end

for i,n in square,3,0
do
   print(i,n)
end

1 1
2 4
3 9

多状态的迭代器

很多情况下,迭代器需要保存多个状态信息而不是简单的状态常量和控制变量,最简单的方法是使用闭包,还有一种方法就是将所有的状态信息封装到 table 内,将 table 作为迭代器的状态常量,因为这种情况下可以将所有的信息存放在 table 内,所以迭代函数通常不需要第二个参数。

以下实例我们创建了自己的迭代器:

array = {"Google", "Runoob"}

function elementIterator (collection)
   local index = 0
   local count = #collection
   -- 闭包函数
   return function ()
      index = index + 1
      if index <= count
      then
         -- 返回迭代器的当前元素
         return collection[index]
      end
   end
end

for element in elementIterator(array)
do
   print(element)
end

Google
Runoob

repeat…until 循环

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

语法

Lua 编程语言中 repeat…until 循环语法格式:

repeat
   statements
until( condition )

我们注意到循环条件判断语句(condition)在循环体末尾部分,所以在条件进行判断前循环体都会执行一次。

如果条件判断语句(condition)为 false,循环会重新开始执行,直到条件判断语句(condition)为 true 才会停止执行。

Lua repeat…until 循环流程图如下:

--[ 变量定义 --]
a = 10
--[ 执行循环 --]
repeat
   print("a的值为:", a)
   a = a + 1
until( a > 15 )

a的值为: 10
a的值为: 11
a的值为: 12
a的值为: 13
a的值为: 14
a的值为: 15

循环嵌套

Lua 编程语言中允许循环中嵌入循环。以下实例演示了 Lua 循环嵌套的应用。

语法

Lua 编程语言中 for 循环嵌套语法格式:

for var=exp1,exp2,exp3 do  
    <执行体>  
    
    for var=exp1,exp2,exp3 do  
    <执行体>  
	
    end  
    
end

Lua 编程语言中 for 循环泛型嵌套语法格式:

for i, v in ipairs(a) do
    print(i, v)
    	
   	 for i1, v1 in ipairs(v) do
  	  print(i1, v1)      
      end  
    
end

Lua 编程语言中 while 循环嵌套语法格式:

while(condition)
do
   while(condition)
   do
      statements
   end
   statements
end

Lua 编程语言中 repeat…until 循环嵌套语法格式:

repeat
   statements
   repeat
      statements
   until( condition )
until( condition )

除了以上同类型循环嵌套外,我们还可以使用不同的循环类型来嵌套,如 for 循环体中嵌套 while 循环。

以下实例使用了for循环嵌套:

j =2
for i=2,10 do
       for j=2,(i/j) , 2 do
              if(not(i%j))
              then
                 break
              end
              if(j > (i/j))then
                 print("i 的值为:",i)
              end
       end
end
break 语句

Lua 编程语言 break 语句插入在循环体中,用于退出当前循环或语句,并开始脚本执行紧接着的语句。

如果你使用循环嵌套,break语句将停止最内层循环的执行,并开始执行的外层的循环语句。

以下实例执行 while 循环,在变量 a 小于 20 时输出 a 的值,并在 a 大于 15 时终止执行循环:

--[ 定义变量 --]
a = 10

--[ while 循环 --]
while( a < 20 )
do
   print("a 的值为:", a)
   a=a+1
       if( a > 15)
       then
          --[ 使用 break 语句终止循环 --]
          break
       end
end

以上代码执行结果如下:

a 的值为: 10
a 的值为: 11
a 的值为: 12
a 的值为: 13
a 的值为: 14
a 的值为: 15

goto 语句

Lua 语言中的 goto 语句允许将控制流程无条件地转到 被标记的语句处 然后继续往下执行。

goto 在 lua 5.2之后支持 lua 5.1 不支持 而且必须在脚本默认下使用 否则无效的

Lua支持goto语法, 但是有一定的局限性.

例如

不能在block外面跳入block(因为block中的lable不可见),
1.
不能跳出或者跳入一个函数.
1.
不能跳入本地变量的作用域.

语法格式如下所示:

goto Label

Label 的格式为:

:: Label ::
local a = 1

::label::    
print("--- goto label ---")

a = a+1
if (a < 3) 
then
    goto label      -- a 小于 3 的时候跳转到标签 label
end

— goto label —
— goto label —

从输出结果可以看出,多输出了一次 — goto label —

以下实例演示了可以在 lable 中设置多个语句:

i = 0
::s1::   --goto跳转位置
do
  print(i)
  i = i+1
end
if i>3 then
  os.exit()   -- i 大于 3 时退出    (os.exit() 直接结束程序   )
end
goto s1

0
1
2
3

有了 goto,我们可以实现 continue 的功能:

for i=1, 3 do
    if i <= 2 then
        print(i, "yes continue")
        goto continue
    end
    print(i, " no continue")
    ::continue::
    print([[i'm end]])
end

1 yes continue
i’m end
2 yes continue
i’m end
3 no continue
i’m end

Lua 函数

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

Lua 提供了许多的内建函数,你可以很方便的在程序中调用它们,如print()函数可以将传入的参数打印在控制台上。

Lua 函数主要有两种用途:

  • 1.完成指定的任务,这种情况下函数作为调用语句使用;
  • 2.计算并返回值,这种情况下函数作为赋值语句的表达式使用。
函数定义

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

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

解析:

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

注意: lua 里调用函数时,即使实参列表和形参列表不一致也能成功调用,多余的参数会被舍弃,缺少的参数会被补为 nil。

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

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

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

   return result;
end

-- 调用函数
print("两值比较最大值为 ",max(10,4))
print("两值比较最大值为 ",max(5,6))

以上代码执行结果为:

两值比较最大值为 10
两值比较最大值为 6

函数传递

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

myprint = function(param)
   print("这是打印函数 - ##",param,"##")
end

function add(num1,num2,functionPrint)
   result = num1 + num2
   -- 调用传递的函数参数
   functionPrint(result)
end

--调用函数
myprint(10)

-- myprint 函数作为参数传递
add(2,5,myprint)

以上代码执行结果为:

这是打印函数 - /#/# 10 /#/#
这是打印函数 - /#/# 7 /#/#

函数返回值

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

下标从1开始计算 (如果不存在匹配串返回nil)。

s, e = string.find("www.runoob.com", "runoob") 
print(s, e)

5 10

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}))

23 3

函数可变参数

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

function add(...)  
local s = 0  
  for i, v in ipairs{...} do   --> {...} 表示一个由所有变长参数构成的数组 
    s = s + v  
  end  
  return s  
end  
print(add(3,4,5,6,7))  --->25

25

我们可以将可变参数赋值给一个变量

例如,我们计算几个数的平均值:

function average(...)
   result = 0
   local arg={...}    --> arg 为一个表,局部变量
   for i,v in ipairs(arg) do
      result = result + v
   end
   print("总共传入 " .. #arg .. " 个数")
   return result/#arg
end

print("平均值为",average(10,5,3,4,5,6))

总共传入 6 个数
平均值为 5.5

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

function average(...)
   result = 0
   local arg={...}
   for i,v in ipairs(arg) do
      result = result + v
   end
   print("总共传入 " .. select("#",...) .. " 个数")
   return result/select("#",...)
end

print("平均值为",average(10,5,3,4,5,6))

总共传入 6 个数
平均值为 5.5

有时候我们可能需要几个固定参数加上可变参数,固定参数必须放在变长参数之前:

function fwrite(fmt, ...)  ---> 固定的参数fmt
    return io.write(string.format(fmt, ...))       --格式字符串输出
end

fwrite("runoob\n")       ---> fmt = "runoob", 没有变长参数。 

fwrite("%d%d\n", 1, 2)   ---> fmt = "%d%d", 参数为 1 和 2

runoob
12

通常在遍历变长参数的时候只需要使用 {…},然而变长参数可能会包含一些 nil,那么就可以用 select 函数来访问变长参数了:select(’/#’, …) 或者 select(n, …)

  • select(’/#’, …) 返回可变参数的长度
  • select(n, …) 用于返回 nselect(’/#’,…) 的参数

调用 select 时,必须传入一个固定实参 selector(选择开关) 和一系列变长参数。如果 selector 为数字 n,那么 select 返回 n 后所有的参数,否则只能为字符串 /#,这样 select 返回变长参数的总数。

do  
    function foo(...)  
        for i = 1, select('#', ...) do  -->获取参数总数
            local arg = select(i, ...); -->读取参数
            print("arg", arg);  
        end  
    end  
 
    foo(1, 2, 3, 4);  
end

arg 1
arg 2
arg 3
arg 4

Lua 元表

在 Lua table 中我们可以访问对应的key来得到value值,但是却无法对两个 table 进行操作。

因此 Lua 提供了元表(Metatable),允许我们改变table的行为,每个行为关联了对应的元方法。

注意: 只能对2个表进行操作

有两个很重要的函数来处理元表:

  • setmetatable(table,metatable): 对指定 table 设置元表(metatable),如果元表(metatable)中存在 __metatable 键值,setmetatable 会失败。
  • getmetatable(table): 返回对象的元表(metatable)。

以下实例演示了如何对指定的表设置元表:

mytable = {}                          -- 表
mymetatable = {}                      -- 元表(子表)
setmetatable(mytable,mymetatable)     -- 设置 mytable的元表为mymetatable

把元表当做成子表就行

返回指定表内的元表:

table=getmetatable(mytable)    -- 返回 mymetatable 这个子表 的引用
a = {"a","b","c"}   
b = {"a1","b1","c1"}   
c = setmetatable(a,b)
print(c[1])  -- a

table=getmetatable(c)  
print(table[1]) --- a1

__index 元方法

那么如果在table中取没有定义的键,那么lua就会在 __index元方法里面去找,前提是 __index是一个表,她还可以是一个函数

table = {a = 1}
setmetatable(table, {__index = {b = 2} })
print(table.b)   --2

如果__index包含一个函数的话,Lua就会调用那个函数,table和键会作为参数传递给函数。

__index 元方法查看表中元素是否存在,如果不存在,返回结果为 nil;如果存在则由 __index 返回结果。

mt={
  __index = function(mytable, key)   --固定写法 
    if key == "key2" then
      return "metatablevalue"
    else
      return nil
    end
  end
}

mytable = setmetatable({key1 = "value1"}, mt)

print(mytable.key1,mytable.key2)

value1 metatablevalue

总结 __index 的用法

Lua 查找一个表元素时的规则,其实就是如下 3 个步骤:

在表中查找,如果找到,返回该元素,找不到则判断该表是否有元表 如果没有元表就返回 nil
*
如果存在元表那么 继续找元表内是否存在 如果不存在那么判断元表内有没有 __index 方法
*
如果没有__index 方法 就返回nil 如果存在 那么就在 __index对应的表里继续找

__newindex 元方法

如果,我们要对table中某个不存在的字段赋值呢?(小若:就,直接赋值啊!)

没错,我们直接就能赋值了,不会报错的。

问题是,如果我想监控这个操作呢?如果有人想对table不存在的字段进行赋值的时候,我想进行一些额外的处理呢?

这时候就要用到__newindex。

大家要记住这句话:__index用于查询,__newindex用于更新

smartMan = {
        name = "none",
        sayHello = function()
            print("大家好,我是聪明的hu。");
        end
}

t1 = {};  --表
  
mt = {  --元表
__index = smartMan,
 __newindex = function(table, key, value)
   print(key .. "字段是不存在的,不要试图给它赋值!");
        end
}
  
setmetatable(t1, mt);  --组装
  
t1.sayHello = function()   --打印: sayHello字段是不存在的,不要试图给它赋值!
   print("en");
end;

t1.sayHello();  -- 大家好,我是聪明的hu。

很显然,sayHello字段赋值失败,因为给sayHello字段赋值的时候,调用了__newindex元方法,代替了赋值操作。

为什么?sayHello字段不是存在的么? 为什么会说不存在呢?

如果表里没有 就会去元表里找 先判断元表有没有 __newindex 方法 如果有 那么就执行这个方法

如果元表里没有 此key 那么就在表在新创建一个key

__newindex元方法被调用的时候会传入3个参数:table本身、字段名、想要赋予的值。

对于__newindex元方法,可以调用rawset(t, k, v)函数,它可以不涉及任何元方法而直接设置table t中与key k相关联的value v。

mytable = setmetatable({key1 = "value1"}, {
  __newindex = function(mytable, key, value)          
            if(key=="key2")then
              rawset(mytable, key, "key2你想赋值"..value.."呵呵这是不可能的") --控制返回结果
             else
              rawset(mytable, key, value) --赋值成功
            end
            
               

  end
})

mytable.key1 = "new value"
mytable.key2 = 4

print(mytable.key1) --new value
print(mytable.key2) --你想赋值4呵呵这是不可能的

mytable.key3 = 10
print(mytable.key3 ) --10

总结__newindex规则

a.如果newindex是一个函数,则在给 表 不存在的字段赋值时,会调用这个函数。
b.如果newindex是一个table,则在给 表 不存在的字段赋值时,会直接给__newindex的table赋值。

为表添加操作符

模式描述
__add对应的运算符 ‘+’.
__sub对应的运算符 ‘-’.
__mul对应的运算符 ‘/*’.
__div对应的运算符 ‘/’.
__mod对应的运算符 ‘%’.
__unm对应的运算符 ‘-’.
__concat对应的运算符 ‘…’.
__eq对应的运算符 ‘==’.
__lt对应的运算符 ‘<’.
__le对应的运算符 ‘<=’.

以下实例演示了两表相加操作 __add:

-- 两表相加操作
mytable = setmetatable({ "a", "b", "c" }, {
  __add = function(mytable, newtable)
    for i = 1, table_maxn(newtable) do
              
      table.insert(mytable,newtable[i])
    end
    return mytable
  end
})

secondtable = {"d","e","f"}

mytable = mytable + secondtable
        for k,v in ipairs(mytable) do
        print(k,v)
end

1 a
2 b
3 c
4 d
5 e
6 f

table.insert(list, [pos, ]value) 方法介绍

list: 要插入的表

pos 可选 插入的位置 不写默认最后一位 +1

value 要插入的值

__call 元方法

__call 元方法在 Lua 调用一个值时调用。以下实例演示了计算 两表中元素的和:

-- 自定义 获取算表中最后键的位置 即计算表的元素个数
function table_maxn(t)
    local mn = 0
    for k, v in pairs(t) do
        if mn < k then
            mn = k
        end
    end
    return mn
end

table={10,20,30}

-- 定义元方法__call
mytable = setmetatable(  table  , {
  __call = function(mytable, newtable)
        sum = 0
        for i = 1, table_maxn(mytable) do
                sum = sum + mytable[i]
        end
    for i = 1, table_maxn(newtable) do
                sum = sum + newtable[i]
        end
        return sum
  end
})
newtable = {1,2,3}
print(mytable(newtable))

66

__tostring 元方法

__tostring 元方法用于修改表的输出行为。以下实例我们自定义了表的输出内容:

默认情况直接调用表会打印地址 我们可以改变这个行为 并做处理

mytable = setmetatable({ 10, 20, 30 }, {
  __tostring = function(mytable)
    sum = 0
    for k, v in pairs(mytable) do
                sum = sum + v
        end
    return "表所有元素的和为 " .. sum
  end
})
print(mytable)   --表所有元素的和为 60

从本文中我们可以知道元表可以很好的简化我们的代码功能,所以了解 Lua 的元表,可以让我们写出更加简单优秀的 Lua 代码。

相关文章