type
status
date
slug
summary
tags
category
icon
password
Property
- 轻量级: 它用标准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
命令后进入lua
的shell
中执行语句。脚本式编程
也可以把脚本存成一个文件来运行。查看 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
语言中的NULL
在Lua
中是nil
,比如访问一个没有声明过的变量,就是nil
,比如下面的v的值就是nil
布尔类型只有
nil
和false
是false
,数字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
MetaTable
和MetaMethod
是Lua
中的重要的语法,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
的模板加上__index
的MetaMethod
来创建另一个实例:于是:
MyWin
中就可以访问x, y, width, height
了。(注:当表要索引一个值时如table[key], Lua会首先在table本身中查找key的值, 如果没有并且这个table存在一个带有__index属性的Metatable, 则Lua会按照__index所定义的函数逻辑查找)上面可以看到有一个
new
方法和一个toString
的方法。其中:1)
self
就是 Person
,Person:new(p)
相当于Person.new(self, p)
2)
new
方法的self.__index = self
的意图是怕self被扩展后改写,所以,让其保持原样3)
setmetatable
这个函数返回的是第一个参数的值。于是:可以这样调用:
Lua
和Javascript
很相似,都是在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
干的事就如下: