1:高效的,轻量级,嵌入式脚本语言;
2: Lua是一个语言标准;
3:脚本语言有对应的解释器(虚拟机),,解释器有两个分支:
官方lua,LuaJIT(高效,即时编译技术);
4: Lua的官方网站;http://www.lua.org/
1:下载LuaDist;
2:加压后放到对应的目录下;
3:将bin目录加入到电脑的环境Path;
4: lua解释器, luac字节码编译器: lua代码--> lua字节码;
第一个lua程序
1:编写main.lua;
2:编写代码print("HelloWorld!!")
3: lua main.lua使用Iua解释器解释执行Iua代码;
4:luac可以将Iua文件编译成Iua字节码;
5: lua执行字节码;
6:每一行代码都是一个语句;
1: Lua基本的数据类型:整数,小数,逻辑数;
2: Lua不分整数和小数;
3: true, false
在默认情况下,变量总是认为是全局的。
全局变量不需要声明,给一个变量赋值后即创建了这个全局变量,访问一个没有初始化的全局变量也不会出错,只不过得到的结果是:nil。
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
字符串由一对双引号或单引号来表示。
string1 = "this is string1"
string2 = 'this is string2'
Lua 中的表(table)其实是一个"关联数组"(associative arrays),数组的索引可以是数字或者是字符串。
table 不会固定长度大小,有新数据添加时 table 长度会自动增长,没初始的 table 都是 nil。
1.local局部变量
1:定义一个局部变量:local变量名称
2:局部变量定义在哪里,那么它的作用范围就在哪里;
3:系统会为局部变量分配一个内存,这个内存只能存基本数据类型或复杂数据类型的引用;
4:变量在运行的时候存的是什么就是什么;
5: print(变量名称),打印一个变量
6:如果变量没有赋值或一个空的值,我们可以用nil来表示;
7:如果是一个不存在的变量或名字,也是nil;
2.
变量在使用前,必须在代码中进行声明,即创建该变量。
编译程序执行代码之前编译器需要知道如何给语句变量开辟存储区,用于存储变量的值。
Lua 变量有三种类型:全局变量、局部变量、表中的域。
Lua 中的变量全是全局变量,那怕是语句块或是函数里,除非用 local 显式声明为局部变量。
局部变量的作用域为从声明位置开始到所在语句块结束。
变量的默认值均为 nil。
-- test.lua 文件脚本
a = 5 -- 全局变量
local b = 5 -- 局部变量function joke()c = 5 -- 全局变量local d = 6 -- 局部变量
endjoke()
print(c,d) --> 5 nildolocal a = 6 -- 局部变量b = 6 -- 对局部变量重新赋值print(a,b); --> 6 6
endprint(a,b) --> 5 6
3.赋值语句
赋值是改变一个变量的值和改变表域的最基本的方法。
Lua 可以对多个变量同时赋值,变量列表和值列表的各个元素用逗号分开,赋值语句右边的值会依次赋给左边的变量。
a, b = 10, 2*x <--> a=10; b=2*x
遇到赋值语句Lua会先计算右边所有的值然后再执行赋值操作,所以我们可以这样进行交换变量的值:
x, y = y, x -- swap &#39;x&#39; for &#39;y&#39;
a[i], a[j] = a[j], a[i] -- swap &#39;a[i]&#39; for &#39;a[j]&#39;
当变量个数和值的个数不一致时,Lua会一直以变量个数为基础采取以下策略:
a. 变量个数 > 值的个数 按变量个数补足nil
b. 变量个数 <值的个数 多余的值会被忽略
a, b, c = 0, 1
print(a,b,c) --> 0 1 nila, b = a+1, b+1, b+2 -- value of b+2 is ignored
print(a,b) --> 1 2a, b, c = 0
print(a,b,c) --> 0 nil nil
上面最后一个例子是一个常见的错误情况,注意:如果要对多个变量赋值必须依次对每个变量赋值。
a, b, c = 0, 0, 0
print(a,b,c) --> 0 0 0
多值赋值经常用来交换变量,或将函数调用返回给变量:
a, b = f()f()返回两个值,第一个赋给a,第二个赋给b。应该尽可能的使用局部变量,有两个好处:1. 避免命名冲突。
2. 访问局部变量的速度比全局变量更快。
索引
t[i]
t.i -- 当索引为字符串类型时的一种简化写法
gettable_event(t,i) -- 采用索引访问本质上是一个类似这样的函数调用> site = {}
> site["key"] = "www.runoob.com"
> print(site["key"])
www.runoob.com
> print(site.key)
www.runoob.com
算术运算符
关系运算符
逻辑运算符
其他运算符
运算符优先级
array = {"Lua", "Tutorial"}for i= 0, 2 doprint(array[i])
end
nil
Lua
Tutorial //从1开始不是0
正如你所看到的,我们可以使用整数索引来访问数组元素,如果知道的索引没有值则返回nil。
在 Lua 索引值是以 1 为起始,但你也可以指定 0 开始。
除此外我们还可以以负数为数组索引值:
array = {}for i= -2, 2 doarray[i] = i *2
endfor i = -2,2 doprint(array[i])
end-4
-2
0
2
4
多维数组
-- 初始化数组
array = {}
for i=1,3 doarray[i] = {}for j=1,3 doarray[i][j] = i*jend
end-- 访问数组
for i=1,3 dofor j=1,3 doprint(array[i][j])end
end
-- 初始化数组
array = {}
maxRows = 3
maxColumns = 3
for row=1,maxRows dofor col=1,maxColumns doarray[row*maxColumns +col] = row*colend
end-- 访问数组
for row=1,maxRows dofor col=1,maxColumns doprint(array[row*maxColumns +col])end
end
1: function name(参数1,参数2..)
end
2:变量可以保存函数对象;
3:return [返回值]:返回语句,可以带返回值
4:函数调用,跳入函数对象,一条一条语句, 遇到return后回到函数开始调用的下一条语句;
--[[ 函数返回两个值的最大值 --]]
function max(num1, num2)if (num1 > num2) thenresult = num1;elseresult = num2;endreturn result;
end
-- 调用函数
print("两值比较最大值为 ",max(10,4))
print("两值比较最大值为 ",max(5,6))
两值比较最大值为 10
两值比较最大值为 6
myprint = function(param)print("这是打印函数 - ##",param,"##")
endfunction add(num1,num2,functionPrint)result = num1 + num2-- 调用传递的函数参数functionPrint(result)
end
myprint(10)
-- myprint 函数作为参数传递
add(2,5,myprint)
这是打印函数 - ## 10 ##
这是打印函数 - ## 7 ##
多返回值
Lua函数可以返回多个结果值,比如string.find,其返回匹配串"开始和结束的下标"(如果不存在匹配串返回nil)。
> s, e = string.find("www.runoob.com", "runoob")
> print(s, e)
5 10
function maximum (a)local mi = 1 -- 最大值索引local m = a[mi] -- 最大值for i,val in ipairs(a) doif val > m thenmi = im = valendendreturn m, mi
endprint(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
function average(...)result = 0local arg={...} --> arg 为一个表,局部变量for i,v in ipairs(arg) doresult = result + vendprint("总共传入 " .. #arg .. " 个数")return result/#arg--return result/select("#",...)
endprint("平均值为",average(10,5,3,4,5,6))
总共传入 6 个数
平均值为 5.5
function fwrite(fmt, ...) ---> 固定的参数fmtreturn io.write(string.format(fmt, ...))
endfwrite("runoob\n") --->fmt = "runoob", 没有变长参数。
fwrite("%d%d\n", 1, 2) --->fmt = "%d%d", 变长参数为 1 和 2
runoob
12
通常在遍历变长参数的时候只需要使用 {…},然而变长参数可能会包含一些 nil,那么就可以用 select 函数来访问变长参数了:select(&#39;#&#39;, …) 或者 select(n, …)
调用select时,必须传入一个固定实参selector(选择开关)和一系列变长参数。如果selector为数字n,那么select返回它的第n个可变实参,否则只能为字符串"#",这样select会返回变长参数的总数。例子代码:
do function foo(...) for i = 1, select(&#39;#&#39;, ...) do -->获取参数总数local arg = select(i, ...); -->读取参数print("arg", arg); end end foo(1, 2, 3, 4);
end
arg 1
arg 2
arg 3
arg 4
1:字符串对象:指向一串文字;
2:它也是一种复杂的数据对象;
string1 = "Lua"
print("\"字符串 1 是\"",string1)
string2 = &#39;runoob.com&#39;
print("字符串 2 是",string2)string3 = [["Lua 教程"]]
print("字符串 3 是",string3)以上代码执行输出结果为:"字符串 1 是" Lua
字符串 2 是 runoob.com
字符串 3 是 "Lua 教程"
转义字符
\a | 响铃(BEL) | 007 |
\b | 退格(BS) ,将当前位置移到前一列 | 008 |
\f | 换页(FF),将当前位置移到下页开头 | 012 |
\n | 换行(LF) ,将当前位置移到下一行开头 | 010 |
\r | 回车(CR) ,将当前位置移到本行开头 | 013 |
\t | 水平制表(HT) (跳到下一个TAB位置) | 009 |
\v | 垂直制表(VT) | 011 |
\\ | 代表一个反斜线字符&#39;&#39;\&#39; | 092 |
\&#39; | 代表一个单引号(撇号)字符 | 039 |
\" | 代表一个双引号字符 | 034 |
\0 | 空字符(NULL) | 000 |
\ddd | 1到3位八进制数所代表的任意字符 | 三位八进制 |
\xhh | 1到2位十六进制所代表的任意字符 | 二位十六进制 |
字符串操作
1 | string.upper(argument): 字符串全部转为大写字母。 |
2 | string.lower(argument): 字符串全部转为小写字母。 |
3 | string.gsub(mainString,findString,replaceString,num) 在字符串中替换。 mainString 为要操作的字符串, findString 为被替换的字符,replaceString 要替换的字符,num 替换次数(可以忽略,则全部替换),如:
|
4 | string.find (str, substr, [init, [end]]) 在一个指定的目标字符串中搜索指定的内容(第三个参数为索引),返回其具体位置。不存在则返回 nil。
|
5 | string.reverse(arg) 字符串反转
|
6 | string.format(...) 返回一个类似printf的格式化字符串
|
7 | string.char(arg) 和 string.byte(arg[,int]) char 将整型数字转成字符并连接, byte 转换字符为整数值(可以指定某个字符,默认第一个字符)。
|
8 | string.len(arg) 计算字符串长度。
|
9 | string.rep(string, n) 返回字符串string的n个拷贝
|
10 | .. 链接两个字符串
|
11 | string.gmatch(str, pattern) 回一个迭代器函数,每一次调用这个函数,返回一个在字符串 str 找到的下一个符合 pattern 描述的子串。如果参数 pattern 描述的字符串没有找到,迭代函数返回nil。
|
12 | string.match(str, pattern, init) string.match()只寻找源字串str中的第一个配对. 参数init可选, 指定搜寻过程的起点, 默认为1。 在成功配对时, 函数将返回配对表达式中的所有捕获结果; 如果没有设置捕获标记, 则返回整个配对字符串. 当没有成功的配对时, 返回nil。
|
字符与整数相互转换
-- 字符转换
-- 转换第一个字符
print(string.byte("Lua")) --76
-- 转换第三个字符
print(string.byte("Lua",3)) --97
-- 转换末尾第一个字符
print(string.byte("Lua",-1)) --97
-- 第二个字符
print(string.byte("Lua",2)) --117
-- 转换末尾第二个字符
print(string.byte("Lua",-2)) --117-- 整数 ASCII 码转换为字符
print(string.char(97)) --a
匹配模式:去百度
Lua字符串对象
迭代器(iterator)是一种对象,它能够用来遍历标准模板库容器中的部分或全部元素,每个迭代器对象代表容器中的确定的地址。在 Lua 中迭代器是一种支持指针类型的结构,它可以遍历集合的每一个元素。
泛型 for 迭代器
泛型 for 在自己内部保存迭代函数,实际上它保存三个值:迭代函数、状态常量、控制变量。
泛型 for 迭代器提供了集合的 key/value 对,语法格式如下:
array = {"Google", "Runoob"}for key,value in ipairs(array)
doprint(key, value)
end》》
1 Google
2 Runoob
无状态的迭代器
无状态的迭代器是指不保留任何状态的迭代器,因此在循环中我们可以利用无状态迭代器避免创建闭包花费额外的代价。
每一次迭代,迭代函数都是用两个变量(状态常量和控制变量)的值作为参数被调用,一个无状态的迭代器只利用这两个值可以获取下一个元素。这种无状态迭代器的典型的简单的例子是 ipairs,它遍历数组的每一个元素。
以下实例我们使用了一个简单的函数来实现迭代器,实现 数字 n 的平方:
function square(iteratorMaxCount,currentNumber)if currentNumber
doprint(i,n)
end》》
1 1
2 4
3 9
迭代的状态包括被遍历的表(循环过程中不会改变的状态常量)和当前的索引下标(控制变量),ipairs 和迭代函数都很简单,我们在 Lua 中可以这样实现:
function iter (a, i)i = i + 1local v = a[i]if v thenreturn i, vend
endfunction ipairs (a)return iter, a, 0
end
当 Lua 调用 ipairs(a) 开始循环时,他获取三个值:迭代函数 iter、状态常量 a、控制变量初始值 0;然后 Lua 调用 iter(a,0) 返回 1, a[1](除非 a[1]=nil);第二次迭代调用 iter(a,1) 返回 2, a[2]……直到第一个 nil 元素。
多状态的迭代器
很多情况下,迭代器需要保存多个状态信息而不是简单的状态常量和控制变量,最简单的方法是使用闭包,还有一种方法就是将所有的状态信息封装到 table 内,将 table 作为迭代器的状态常量,因为这种情况下可以将所有的信息存放在 table 内,所以迭代函数通常不需要第二个参数。以下实例我们创建了自己的迭代器:
array = {"Google", "Runoob"}function elementIterator (collection)local index = 0local count = #collection-- 闭包函数return function ()index = index + 1if index <= countthen-- 返回迭代器的当前元素return collection[index]endend
endfor element in elementIterator(array)
doprint(element)
end
Google
Runoob
基本表达式
1:=, +, *,/,赋值,加减剩除;
2:()改变运算的优先级;
3:字符串对象加法 ..
5: lua没有c/c++的缩写表达式+=-=*=,++.-;
条件语句
1:if then end:如果条件成立,就执行if和end之间的代码块
2:if then else end如果条件成立执行i,否则的话执行else
3: if then elseif then end多个条件判断;
循环语句
1: for循环语句
for exp1(开始), exp2(结束), exp3(步长) do
end
步长可以省略,默认为1;bi&#39;ch
2: while循环语句
while条件then
end
数值for循环for i=10,1,-1 doprint(i)
end泛型for循环--打印数组a的所有值
a = {"one", "two", "three"}
for i, v in ipairs(a) doprint(i, v)
end
a=10
while( a <20 )
doprint("a 的值为:", a)a = a+1
end
Lua 循环嵌套
j =2
for i=2,10 dofor j=2,(i/j) , 2 doif(not(i%j))thenbreakendif(j > (i/j))thenprint("i 的值为:",i)endend
end》》》
i 的值为: 8
i 的值为: 9
i 的值为: 10
Lua repeat...until 循环
--[ 变量定义 --]
a = 10
--[ 执行循环 --]
repeatprint("a的值为:", a)a = a + 1
until( a > 15 )》》
a的值为: 10
a的值为: 11
a的值为: 12
a的值为: 13
a的值为: 14
a的值为: 15
1:定义一个表{key= value, key2 = value, ...}
2: key可以是整数,字符串
3: value可以是任意类型;
4:访问value的每个元素 表[key],表.key
5: Lua没有数组,只有表;
table 是 Lua 的一种数据结构用来帮助我们创建不同的数据类型,如:数组、字典等。
Lua table 使用关联型数组,你可以用任意类型的值来作数组的索引,但这个值不能是 nil。
Lua table 是不固定大小的,你可以根据自己需要进行扩容。
Lua也是通过table来解决模块(module)、包(package)和对象(Object)的。 例如string.format表示使用"format"来索引table string。
-- 初始化表
mytable = {}-- 指定值
mytable[1]= "Lua"-- 移除引用
mytable = nil
-- 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 = mytableprint("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
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进行升序排序。 |
模块类似于一个封装库,从 Lua 5.1 开始,Lua 加入了标准的模块管理机制,可以把一些公用的代码放在一个文件里,以 API 接口的形式在其他地方调用,有利于代码的重用和降低代码耦合度。
Lua 的模块是由变量、函数等已知元素组成的 table,因此创建一个模块很简单,就是创建一个 table,然后把需要导出的常量、函数放入其中,最后返回这个 table 就行。以下为创建自定义模块 module.lua,文件代码格式如下:
-- 文件名为 module.lua
-- 定义一个名为 module 的模块
module = {}-- 定义一个常量
module.cOnstant= "这是一个常量"-- 定义一个函数
function module.func1()io.write("这是一个公有函数!\n")
endlocal function func2()print("这是一个私有函数!")
endfunction module.func3()func2()
endreturn module
2.
对于自定义的模块,模块文件不是放在哪个文件目录都行,函数 require 有它自己的文件路径加载策略,它会尝试从 Lua 文件或 C 程序库中加载模块。
require 用于搜索 Lua 文件的路径是存放在全局变量 package.path 中,当 Lua 启动后,会以环境变量 LUA_PATH 的值来初始这个环境变量。如果没有找到该环境变量,则使用一个编译时定义的默认路径来初始化。
当然,如果没有 LUA_PATH 这个环境变量,也可以自定义设置,在当前用户根目录下打开 .profile 文件(没有则创建,打开 .bashrc 文件也可以),例如把 "~/lua/" 路径加入 LUA_PATH 环境变量里:
#LUA_PATH
export LUA_PATH="~/lua/?.lua;;"
文件路径以 ";" 号分隔,最后的 2 个 ";;" 表示新加的路径后面加上原来的默认路径。
接着,更新环境变量参数,使之立即生效。
source ~/.profile
如果找过目标文件,则会调用 package.loadfile 来加载模块。否则,就会去找 C 程序库。
搜索的文件路径是从全局变量 package.cpath 获取,而这个变量则是通过环境变量 LUA_CPATH 来初始。
搜索的策略跟上面的一样,只不过现在换成搜索的是 so 或 dll 类型的文件。如果找得到,那么 require 就会通过 package.loadlib 来加载它。
Lua和C是很容易结合的,使用 C 为 Lua 写包。
与Lua中写包不同,C包在使用以前必须首先加载并连接,在大多数系统中最容易的实现方式是通过动态连接库机制。
Lua在一个叫loadlib的函数内提供了所有的动态连接的功能。这个函数有两个参数:库的绝对路径和初始化函数。所以典型的调用的例子如下:
local path = "/usr/local/lua/lib/libluasocket.so"
local f = loadlib(path, "luaopen_socket")
loadlib 函数加载指定的库并且连接到 Lua,然而它并不打开库(也就是说没有调用初始化函数),反之他返回初始化函数作为 Lua 的一个函数,这样我们就可以直接在Lua中调用他。
如果加载动态库或者查找初始化函数时出错,loadlib 将返回 nil 和错误信息。我们可以修改前面一段代码,使其检测错误然后调用初始化函数:
local path = "/usr/local/lua/lib/libluasocket.so"
-- 或者 path = "C:\\windows\\luasocket.dll",这是 Window 平台下
local f = assert(loadlib(path, "luaopen_socket"))
f() -- 真正打开库
一般情况下我们期望二进制的发布库包含一个与前面代码段相似的 stub 文件,安装二进制库的时候可以随便放在某个目录,只需要修改 stub 文件对应二进制库的实际路径即可。
将 stub 文件所在的目录加入到 LUA_PATH,这样设定后就可以使用 require 函数加载 C 库了。
在 Lua table 中我们可以访问对应的key来得到value值,但是却无法对两个 table 进行操作。
因此 Lua 提供了元表(Metatable),允许我们改变table的行为,每个行为关联了对应的元方法。
例如,使用元表我们可以定义Lua如何计算两个table的相加操作a+b。
当Lua试图对两个表进行相加时,先检查两者之一是否有元表,之后检查是否有一个叫"__add"的字段,若找到,则调用对应的值。"__add"等即时字段,其对应的值(往往是一个函数或是table)就是"元方法"。
有两个很重要的函数来处理元表:
mytable = {} -- 普通表
mymetatable = {} -- 元表
setmetatable(mytable,mymetatable) -- 把 mymetatable 设为 mytable 的元表
以上代码也可以直接写成一行:
mytable = setmetatable({},{})
以下为返回对象元表:
getmetatable(mytable) -- 这回返回mymetatable
相当于:
、