简介
Lua 是一种轻量小巧的脚本语言,用标准C语言编写并以源代码形式开放, 其设计目的是为了嵌入应用程序中,从而为应用程序提供灵活的扩展和定制功能。
Lua 特性
- 轻量级: 它用标准C语言编写并以源代码形式开放,编译后仅仅一百余K,可以很方便的嵌入别的程序里。
- 可扩展: Lua提供了非常易于使用的扩展接口和机制:由宿主语言(通常是C或C++)提供这些功能,Lua可以使用它们,就像是本来就内置的功能一样。
- 其它特性:
支持面向过程(procedure-oriented)编程和函数式编程(functional programming);
自动内存管理;只提供了一种通用类型的表(table),用它可以实现数组,哈希表,集合,对象;
语言内置模式匹配;闭包(closure);函数也可以看做一个值;提供多线程(协同进程,并非操作系统所支持的线程)支持;
通过闭包和table可以很方便地支持面向对象编程所需要的一些关键机制,比如数据抽象,虚函数,继承和重载等。
Lua 应用场景
- 游戏开发
- 独立应用脚本
- Web 应用脚本
- 扩展和数据库插件如:MySQL Proxy 和 MySQL WorkBench
- 安全系统,如入侵检测系统
Lua 环境安装
参考:
Lua 基本语法
交互式编程
1 | # 启用 |
脚本式编程
将 Lua 程序代码保存到一个以 lua 结尾的文件,并执行,该模式称为脚本式编程
hello.lua
1 | print("Hello World!") |
执行:
1 | $ lua hello.lua |
也可以将代码修改为如下形式来执行脚本(在开头添加:#!/usr/local/bin/lua
),指定了 Lua 的解释器 /usr/local/bin directory
。加上 # 号标记解释器会忽略它。接下来我们为脚本添加可执行权限,并执行:
1 |
|
执行:
1 | ./hello.lua |
注释
单行注释:
两个减号是单行注释: –
多行注释:
1 | --[[ |
多行注释推荐使用 –[=[注释内容]=],这样可以避免遇到 table[table[idx]] 时就将多行注释结束了。
注意:多行注释加 - 取消注释中间代码可以继续运行,单行注释没有此功能。
标示符
标示符以一个字母 A 到 Z 或 a 到 z 或下划线 _ 开头后加上 0 个或多个字母,下划线,数字(0 到 9)。
最好不要使用下划线加大写字母的标示符,因为Lua的保留字也是这样的。
Lua 不允许使用特殊字符如 @, $, 和 % 来定义标示符。 Lua 是一个区分大小写的编程语言。
关键词
1 | and break do else |
一般约定,以下划线开头连接一串大写字母的名字(比如 _VERSION)被保留用于 Lua 内部全局变量。
全局变量
在默认情况下,变量总是认为是全局的。
全局变量不需要声明,给一个变量赋值后即创建了这个全局变量,访问一个没有初始化的全局变量也不会出错,只不过得到的结果是:nil。
如果你想删除一个全局变量,只需要将变量赋值为nil。当且仅当一个变量不等于nil时,这个变量即存在。
1 | print(b) |
数据类型
Lua 是动态类型语言,变量不要类型定义,只需要为变量赋值。 值可以存储在变量中,作为参数传递或结果返回。
Lua 中有 8 个基本类型分别为:nil、boolean、number、string、userdata、function、thread 和 table。
1 | print(type("Hello world")) --> string |
nil(空)
1 | > print(type(a)) |
nil 作比较时应该加上双引号 “:
1 | > type(X) |
type(X)==nil 结果为 false 的原因是因为 type(type(X))==string。
boolean(布尔)
boolean 类型只有两个可选值:true(真) 和 false(假),Lua 把 false 和 nil 看作是 false,其他的都为 true,数字 0 也是 true:
number(数字)
Lua 默认只有一种 number 类型 – double(双精度)类型(默认类型可以修改 luaconf.h 里的定义),以下几种写法都被看作是 number 类型:
1 | print(type(2)) |
string(字符串)
字符串由一对双引号或单引号来表示。
1 | string1 = "this is string1" |
也可以用 2 个方括号 “[[]]” 来表示”一块”字符串。
1 | html = [[ |
对一个数字字符串上进行算术操作时,Lua 会尝试将这个数字字符串转成一个数字:
1 | > print("2" + 6) |
使用 # 来计算字符串的长度,放在字符串前面,如下实例:
1 | > len = "www.runoob.com" |
运行时,Lua会自动在string和numbers之间自动进行类型转换,当一个字符串使用算术操作符时, string 就会被转成数字。反过来,当 Lua 期望一个 string 而碰到数字时,会将数字转成 string。
table(表)
在 Lua 里,table 的创建是通过”构造表达式”来完成,最简单构造表达式是{},用来创建一个空表。也可以在表里添加一些数据,直接初始化表:
1 | -- 创建一个空的 table |
Lua 中的表(table)其实是一个”关联数组”(associative arrays),数组的索引可以是数字或者是字符串。
1 | -- table_test.lua 脚本文件 |
脚本执行结果为:
1 | $ lua table_test.lua |
不同于其他语言的数组把 0 作为数组的初始索引,在 Lua 里表的默认初始索引一般以 1 开始。
1 | -- table_test2.lua 脚本文件 |
脚本执行结果为:
1 | $ lua table_test2.lua |
table 不会固定长度大小,有新数据添加时 table 长度会自动增长,没初始的 table 都是 nil。
1 | -- table_test3.lua 脚本文件 |
脚本执行结果为:
1 | $ lua table_test3.lua |
function(函数)
在 Lua 中,函数是被看作是”第一类值(First-Class Value)”,函数可以存在变量里:
1 | -- function_test.lua 脚本文件 |
脚本执行结果为:
1 | $ lua function_test.lua |
function 可以以匿名函数(anonymous function)的方式通过参数传递:
1 | -- function_test2.lua 脚本文件 |
脚本执行结果为:
1 | $ lua function_test2.lua |
thread(线程)
在 Lua 里,最主要的线程是协同程序(coroutine)。它跟线程(thread)差不多,拥有自己独立的栈、局部变量和指令指针,可以跟其他协同程序共享全局变量和其他大部分东西。
线程跟协程的区别:线程可以同时多个运行,而协程任意时刻只能运行一个,并且处于运行状态的协程只有被挂起(suspend)时才会暂停。
userdata(自定义类型)
userdata 是一种用户自定义数据,用于表示一种由应用程序或 C/C++ 语言库所创建的类型,可以将任意 C/C++ 的任意数据类型的数据(通常是 struct 和 指针)存储到 Lua 变量中调用。
Lua 变量
变量在使用前,需要在代码中进行声明,即创建该变量。
编译程序执行代码之前编译器需要知道如何给语句变量开辟存储区,用于存储变量的值。
Lua 变量有三种类型:全局变量、局部变量、表中的域。
Lua 中的变量全是全局变量,那怕是语句块或是函数里,除非用 local 显式声明为局部变量。
局部变量的作用域为从声明位置开始到所在语句块结束。
变量的默认值均为 nil。
尽可能的使用局部变量,有两个好处:
- 避免命名冲突。
- 访问局部变量的速度比全局变量更快。
1 | -- test.lua 文件脚本 |
赋值语句
1 | a = "hello" .. "world" |
Lua 可以对多个变量同时赋值,变量列表和值列表的各个元素用逗号分开,赋值语句右边的值会依次赋给左边的变量。
1 | a, b = 10, 2*x <--> a=10; b=2*x |
遇到赋值语句Lua会先计算右边所有的值然后再执行赋值操作,所以我们可以这样进行交换变量的值:
1 | x, y = y, x -- swap 'x' for 'y' |
当变量个数和值的个数不一致时,Lua会一直以变量个数为基础采取以下策略:
1 | a. 变量个数 > 值的个数 按变量个数补足nil |
1 | a, b, c = 0, 1 |
Lua 对多个变量同时赋值,不会进行变量传递,仅做值传递:
1 | a, b = 0, 1 |
索引
对 table 的索引使用方括号 []。Lua 也提供了 .
操作。
1 | t[i] |
1 | > site = {} |
循环
while 循环
while 循环语法:
1 | while(condition) |
for 循环
for语句有两大类::
数值for循环
泛型for循环
数值for循环
1 | for var=exp1,exp2,exp3 do |
var 从 exp1 变化到 exp2,每次变化以 exp3 为步长递增 var,并执行一次 “执行体”。exp3 是可选的,如果不指定,默认为1。
for的三个表达式在循环开始前一次性求值,以后不再进行求值。比如上面的f(x)只会在循环开始前执行一次,其结果用在后面的循环中。
1 | #!/usr/local/bin/lua |
输出:
1 | function |
泛型for循环
泛型 for 循环通过一个迭代器函数来遍历所有值,类似 java 中的 foreach 语句。迭代的下标是从1开始。
Lua 编程语言中泛型 for 循环语法格式:
1 | --打印数组a的所有值 |
i是数组索引值,v是对应索引的数组元素值。ipairs是Lua提供的一个迭代器函数,用来迭代数组。
for 循环中,循环的索引 i 为外部索引,修改循环语句中的内部索引 i,不会影响循环次数:
1 | for i=1,10 do |
仍然循环 10 次,只是 i 的值被修改了。
在 lua 中 pairs 与 ipairs 两个迭代器的用法相近,但有一点是不一样的:
pairs 能迭代所有键值对。
ipairs 可以想象成 int+pairs,只会迭代键为数字的键值对,只能遍历所有数组下标的值。
如果 ipairs 在迭代过程中是会直接跳过所有手动设定key值的变量。
pairs 可以遍历表中所有的 key,并且除了迭代器本身以及遍历表本身还可以返回 nil;
但是 ipairs 则不能返回nil,只能返回数字0,如果遇到nil则退出。它只能遍历到表中出现的第一个不是整数的key。
例如:
1 | tab = {1,2,a= nil,"d"} |
输出结果为:
1 | 1 1 |
这里是直接跳过了 a=nil 这个变量
第二,ipairs 在迭代过程中如果遇到nil时会直接停止。
1 | tab = {1,2,a= nil,nil,"d"} |
输出结果为:
1 | 1 1 |
这里会在遇到 nil 的时候直接跳出循环。
repeat…until 循环
repeat…until 循环的条件语句在当前循环结束后判断。
1 | repeat |
1 | --[ 变量定义 --] |
输出:
1 | a的值为: 10 |
循环控制语句
break 语句
1 | --[ 定义变量 --] |
goto 语句
Lua 语言中的 goto 语句允许将控制流程无条件地转到被标记的语句处。
语法格式如下所示:
1 | goto Label |
Label 的格式为:
1 | :: Label :: |
以下实例在判断语句中使用 goto:
1 | local a = 1 |
输出结果为:
1 | --- goto label --- |
从输出结果可以看出,多输出了一次 — goto label —。
以下实例演示了可以在 lable 中设置多个语句:
1 | i = 0 |
输出结果为:
1 | 0 |
continue功能
1 | for i=1, 3 do |
输出:
1 | 1 yes continue |
流程控制
if 语句
1 | --[ 定义变量 --] |
if…else 语句
1 | --[ 定义变量 --] |
if…elseif…else 语句
1 | --[ 定义变量 --] |
函数
在Lua中,函数是对语句和表达式进行抽象的主要方法。既可以用来处理一些特殊的工作,也可以用来计算一些值。
Lua 提供了许多的内建函数,你可以很方便的在程序中调用它们,如 print() 函数可以将传入的参数打印在控制台上。
Lua 函数主要有两种用途:
- 1.完成指定的任务,这种情况下函数作为调用语句使用;
- 2.计算并返回值,这种情况下函数作为赋值语句的表达式使用。
函数定义
1 | optional_function_scope function function_name( argument1, argument2, argument3..., argumentn) |
解析:
optional_function_scope: 该参数是可选的制定函数是全局函数还是局部函数,未设置该参数默认为全局函数,如果你需要设置函数为局部函数需要使用关键字 local。
function_name: 指定函数名称。
argument1, argument2, argument3…, argumentn: 函数参数,多个参数以逗号隔开,函数也可以不带参数。
function_body: 函数体,函数中需要执行的代码语句块。
result_params_comma_separated: 函数返回值,Lua语言函数可以返回多个值,每个值以逗号隔开。
将函数作为参数传递给函数,如下实例:
1 | myprint = function(param) |
以上代码执行结果为:
1 | 这是打印函数 - ## 10 ## |
多返回值
Lua函数可以返回多个结果值,比如string.find,其返回匹配串”开始和结束的下标”(如果不存在匹配串返回nil)。
1 | > s, e = string.find("www.runoob.com", "runoob") |
Lua函数中,在return后列出要返回的值的列表即可返回多值,如:
1 | function maximum (a) |
以上代码执行结果为:
1 | 23 3 |
可变参数
Lua 函数可以接受可变数目的参数,和 C 语言类似,在函数参数列表中使用三点 … 表示函数有可变的参数。
1 | function add(...) |
通过 select("#",...)
来获取可变参数的数量:
1 | function average(...) |
以上代码执行结果为:
1 | 总共传入 6 个数 |
如果是几个固定参数加上可变参数,固定参数必须放在变长参数之前
1 | function fwrite(fmt, ...) ---> 固定的参数fmt |
通常在遍历变长参数的时候只需要使用 {…},然而变长参数可能会包含一些 nil,那么就可以用 select 函数来访问变长参数了:select(‘#’, …) 或者 select(n, …)
- select(‘#’, …) 返回可变参数的长度
- select(n, …) 用于返回 n 到 select(‘#’,…) 的参数
调用 select 时,必须传入一个固定实参 selector(选择开关)和一系列变长参数。如果selector 为数字n,那么 select 返回它的第n个可变实参,否则只能为字符串”#”,这样 select 会返回变长参数的总数。例子代码:
1 | do |
输出结果为:
1 | arg 1 |
1 | select(n,...) --> 返回的是多个参数,而不是一个table |
注意:多返回值的函数在赋值时的情况,仅仅只有放在所有逗号之后的那个函数会把返回值展开。
这里列举一个典型情况:
1 | function add() |
运算符
算术运算符
关系运算符
逻辑运算符
其他运算符
1 | a = "Hello " |
输出:
1 | 连接字符串 a 和 b Hello World |
#
获取表的最大索引的值。
1 | tab1 = {"1","2"} |
输出:
1 | tab1长度2 |
下标越过 1 位以上,长度还是为 2:
1 | tab3={} |
输出:
1 | tab3的长度 2 |
三元表达式
利用 and 和 or 的特性,若(A and B) A 为 false 返回 A,(A or B)A 为 false 返回 B,以及除 nil 外其他数据类型被当做 true。
可以非常简单的完成:
1 | local isAppel = false |
运算符优先级
从高到低的顺序:
1 | ^ |
除了 ^ 和 .. 外所有的二元运算符都是左连接的。
1 | a+i < b/2+1 <--> (a+i) < ((b/2)+1) |
1 | a = 20 |
输出:
1 | (a + b) * c / d 运算值为 : 90.0 |
字符串
字符串或串(String)是由数字、字母、下划线组成的一串字符。
Lua 语言中字符串可以使用以下三种方式来表示:
- 单引号间的一串字符。
- 双引号间的一串字符。
- [[ 与 ]] 间的一串字符。
转义字符用于表示不能直接显示的字符,比如后退键,回车键,等。如在字符串转换双引号可以使用 “"“。
字符串操作
1 | string.upper(argument) -- 字符串全部转为大写字母。 |
字符串截取
字符串截取使用 sub() 方法。
1 | string.sub() 用于截取字符串,原型为: |
参数说明:
- s:要截取的字符串。
- i:截取开始位置。
- j:截取结束位置,默认为 -1,最后一个字符。
字符串大小写转换
1 | string1 = "Lua"; |
字符串查找与反转
1 | string = "Lua Tutorial" |
字符串格式化
string.format() 函数来生成具有特定格式的字符串
字符与整数相互转换
1 | -- 字符转换 |
输出:
1 | 76 |
其他常用函数
1 | - 字符串长度 |
匹配模式
Lua 中的匹配模式直接用常规的字符串来描述。 它用于模式匹配函数 string.find, string.gmatch, string.gsub, string.match。
table(表)
1 | -- 初始化表 |
参考: