Lua
2023-4-11
| 2023-8-2
0  |  阅读时长 0 分钟
type
status
date
slug
summary
tags
category
icon
password
Property

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

运行

Lua是类C的,所以,他是大小写字符敏感的。
下面是Lua的Hello World。注意:Lua脚本的语句的分号是可选的,这个和GO语言很类似。
 
在交互模式编程
可以像python一样,在命令行上运行lua命令后进入luashell中执行语句。
 
脚本式编程
也可以把脚本存成一个文件来运行。查看 lua 的编译路径:
将如下代码存储在名为hello.lua的脚本文件中:
 
或是像shell一样运行:
 

语法

注释

两个减号是单行注释:
多行注释:
 

标示符

Lua标示符用于定义一个变量,函数获取其他用户定义的项。标示符以一个字母 A 到 Z 或 a 到 z 或下划线 _ 开头后加上 0 个或多个字母,下划线,数字(0 到 9)。
最好不要使用下划线加大写字母的标示符,因为Lua的保留字也是这样的。
Lua不允许使用特殊字符如 @$, 和 % 来定义标示符。 Lua 是一个区分大小写的编程语言。因此在 Lua 中 A 与 a 是两个不同的字符。
以下列出了Lua的保留关键词。保留关键字不能作为常量或变量或其他用户自定义标示符:
关键字
关键字
关键字
关键字
and
break
do
else
elseif
end
false
for
function
if
in
local
nil
not
or
repeat
return
then
true
until
while
goto
 

变量

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
表示执行的独立线路,用于执行协同程序
table
Lua 中的表其实是一个"关联数组"数组的索引可以是数字、字符串或表类型。在 Lua 里,table 的创建是通过"构造表达式"来完成,最简单构造表达式是{},用来创建一个空表。
 
Lua的数字只有double型,64bits,你不必担心Lua处理浮点数会慢(除非大于100,000,000,000,000),或是会有精度问题。
可以以如下的方式表示数字,0x开头的16进制和C是很像的。
 
字符串可以用单引号,也可以用双引号,还支持C类型的转义,比如: ‘\a’ (响铃), ‘\b’ (退格), ‘\f’ (表单), ‘\n’ (换行), ‘\r’ (回车), ‘\t’ (横向制表), ‘\v’ (纵向制表), ‘\\’ (反斜杠), ‘\”‘ (双引号), 以及 ‘\” (单引号)
 
下面的四种方式定义了完全相同的字符串(其中的两个中括号可以用于定义有换行的字符串)
 
C语言中的NULLLua中是nil,比如访问一个没有声明过的变量,就是nil,比如下面的v的值就是nil
 
布尔类型只有nilfalsefalse,数字0,‘’空字符串(’\0’)都是true!
 
局部变量和全局变量
注意:lua中的变量如果没有特殊说明,全是全局变量,那怕是语句块或是函数里。变量前加local关键字的是局部变量。
 

运算符

运算符是一个特殊的符号,用于告诉解释器执行特定的数学或逻辑运算。Lua提供了以下几种运算符类型:
  • 算术运算符(设定 A 的值为10,B 的值为20)
    • 操作符
      描述
      实例
      +
      加法
      A + B 输出结果 30
      -
      减法
      A - B 输出结果 -10
      *
      乘法
      A * B 输出结果 200
      /
      除法
      B / A 输出结果 2
      %
      取余
      B % A 输出结果 0
      ^
      乘幂
      A^2 输出结果 100
      -
      负号
      -A 输出结果 -10
  • 关系运算符(设定 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 的值为 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…b ,其中 a 为 "Hello " , b 为 “World”, 输出结果为 “Hello World”。
      #
      一元运算符,返回字符串或表的长度。
      #“Hello” 返回 5
 

控制语句

赋值语句

Lua可以对多个变量同时赋值,变量列表和值列表的各个元素用逗号分开,赋值语句右边的值会依次赋给左边的变量。
当变量个数和值的个数不一致时,Lua会一直以变量个数为基础采取以下策略:
  • 变量个数 > 值的个数,按变量个数补足nil
  • 变量个数 < 值的个数,多余的值会被忽略
在实际中,如果要对多个变量赋值必须依次对每个变量赋值。 (避免后期出问题)
多值赋值经常用来交换变量,或将函数调用返回给变量: 比如有一个函数 f( ) 返回了两个值
f()返回两个值,第一个赋给a,第二个赋给b。
 

循环

Lua语言提供了以下几种循环处理方式:
循环类型
描述
while 循环
在条件为 true 时,让程序重复地执行某些语句。执行语句前会先检查条件是否为 true。
for 循环
重复执行指定语句,重复次数可在 for 语句中控制。
repeat…until
重复执行循环,直到 指定的条件为真时为止
循环嵌套
可以在循环内嵌套一个或多个循环语句(while、for、do…while)
Lua 支持以下循环控制语句:
控制语句
描述
break 语句
退出当前循环或语句,并开始脚本执行紧接着的语句。
goto 语句
将程序的控制点转移到一个标签处。
 
while循环语法
 
 
for 循环
Lua 编程语言中 for语句有两大类:
  • 数值for循环
    • var 从 exp1 变化到 exp2,每次变化以 exp3 为步长递增 var,并执行一次 “执行体”。exp3 是可选的,如果不指定,默认为1递增。
      for的三个表达式在循环开始前一次性求值,以后不再进行求值。比如上面的f(x)只会在循环开始前执行一次,其结果用在后面的循环中。
  • 泛型for循环
    • 泛型 for 在自己内部保存迭代函数,实际上它保存三个值:迭代函数、状态常量、控制变量。
      泛型 for 迭代器提供了集合的 key/value 对,语法格式如下:
 
 
repeat…until 循环
lua编程语言中 repeat…until 循环语句不同于 for 和 while循环,for 和 while 循环的条件语句在当前循环执行开始时判断,而 repeat…until 循环的条件语句在当前循环结束后判断。 和do while 差不多:
 
循环嵌套
Lua 编程语言中允许循环中嵌入循环。
 
 

判断

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

函数

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

函数定义

Lua 编程语言函数定义格式如下:
  • optional_function_scope: 该参数是可选的制定函数是全局函数还是局部函数,未设置该参数默认为全局函数,如果你需要设置函数为局部函数需要使用关键字 local
  • function_name: 指定函数名称。
  • argument1, argument2, …: 函数参数,多个参数以逗号隔开,函数也可以不带参数。
  • function_body: 函数体,函数中需要执行的代码语句块。
  • result_params_comma_separated: 函数返回值,Lua语言函数可以返回多个值,每个值以逗号隔开。
 
lua里调用函数时,即使实参列表和形参列表不一致也能成功调用,多余的参数会被舍弃,缺少的参数会被补为nil
定义函数max(),参数为 num1, num2,用于比较两值的大小,并返回最大值:
 

函数传递

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

函数返回值

Lua函数可以返回多个结果值,比如string.find() 这个内置函数,其返回匹配串"开始和结束的下标”
 
Lua函数中,在return后列出要返回的值的列表即可返回多值,如:
 

函数可变参数

C语言类似,在函数参数列表中使用三点  表示函数有可变的参数
 
可以将可变参数赋值给一个变量
 
也可以通过 select("#",…) 来获取可变参数的数量
 
可能需要几个固定参数加上可变参数,固定参数必须放在变长参数之前:
 
通常在遍历变长参数的时候只需要使用 {…},然而变长参数可能会包含一些 nil,那么就可以用 select 函数来访问变长参数了:select(’#’, …) 或者 select(n, …)
  • select(’#’, …) 返回可变参数的长度
  • select(n, …) 用于返回 n 在 select(’#’,…) 的参数
 
调用 select 时,必须传入一个固定实参 selector(选择开关) 和一系列变长参数。如果 selector 为数字 n,那么 select 返回 n 后所有的参数,否则只能为字符串 #,这样 select 返回变长参数的总数。
 

递归

 

闭包

同样,Javascript附体!
 
 
 

局部函数

函数前面加上local就是局部函数,Lua中的函数和Javascript中的一个德行。
比如:下面的两个函数是一样的:
 

Table

所谓Table其实就是一个Key Value的数据结构,它很像Javascript中的Object,或是PHP中的数组,在别的语言里叫Dict或Map,Table长成这个样子:
 
下面是table的CRUD操作:
上面看上去像C/C++中的结构体,但是name,age, handsome, website都是key。还可以像下面这样写义Table:
这样就更像Key Value了。于是你可以这样访问:t[20],t[“name”], t[3.14]。
 
我们再来看看数组:
 
这样看上去就像数组了。但其实其等价于:
所以,你也可以定义成不同的类型的数组,比如:
注:其中的函数可以这样调用:arr[4]()
可以看到Lua的下标不是从0开始的,是从1开始的。
注:上面的程序中:#arr的意思就是arr的长度。
注:前面说过,Lua中的变量,如果没有local关键字,全都是全局变量,Lua也是用Table来管理全局变量的,Lua把这些全局变量放在了一个叫“_G”的Table里。
可以用如下的方式来访问一个全局变量(假设我们这个全局变量名叫globalVar):
可以通过下面的方式来遍历一个Table。
 
 

MetaTable 和 MetaMethod

MetaTableMetaMethodLua中的重要的语法,MetaTable主要是用来做一些类似于C++重载操作符式的功能。
比如,有两个分数:
想实现分数间的相加:2/3 + 4/7,如果要执行: fraction_a + fraction_b,会报错的。
所以,可以动用MetaTable,如下所示:
为之前定义的两个table设置MetaTable:(其中的setmetatble是库函数)
于是就可以这样干了:(调用的是fraction_op.__add()函数)
 
至于__add这是MetaMethod,这是Lua内建约定的,其它的还有如下的MetaMethod:
 

“面向对象”

上面看到有__index这个重载,这个东西主要是重载了find key的操作。这操作可以让Lua变得有点面向对象的感觉,让其有点像Javascript的prototype。
所谓__index,说得明确一点,如果有两个对象a和b,想让b作为a的prototype只需要:
 
例如下面的示例:可以用一个Window_Prototype的模板加上__indexMetaMethod来创建另一个实例:
于是:MyWin中就可以访问x, y, width, height了。(注:当表要索引一个值时如table[key], Lua会首先在table本身中查找key的值, 如果没有并且这个table存在一个带有__index属性的Metatable, 则Lua会按照__index所定义的函数逻辑查找)
 
上面可以看到有一个new方法和一个toString的方法。其中:
1)self 就是 PersonPerson:new(p)相当于Person.new(self, p)
2)new方法的self.__index = self 的意图是怕self被扩展后改写,所以,让其保持原样
3)setmetatable这个函数返回的是第一个参数的值。
于是:可以这样调用:
LuaJavascript很相似,都是在Prototype的实例上改过来改过去的。
 

模块

我们可以直接使用require(“model_name”)来载入别的lua文件,文件的后缀是.lua。载入的时候就直接执行那个文件了。
有一个hello.lua的文件:
如果:require(“hello”),那么就直接输出Hello, World!了。
注意:
1)require函数,载入同样的lua文件时,只有第一次的时候会去执行,后面的相同的都不执行了。
2)如果要让每一次文件都会执行的话,可以使用dofile(“hello”)函数
3)如果要玩载入后不执行,等需要的时候执行时,可以使用 loadfile()函数,如下所示:
loadfile(“hello”)后,文件并不执行,我们把文件赋给一个变量hello,当hello()时,才真的执行。
当然,更为标准的玩法如下所示。假设有一个文件叫mymod.lua
于是可以这样使用:
其实,require干的事就如下:
  • Redis
  • 事务Lua脚本
    目录