一、Lua安装及常用库的安装

1.1 Ubuntu

1
$ sudo apt-get install lua5.2

1.2 OS X

1
2
$ brew install lua luarocks                 # luarocks是lua的模块管理工具
$ sudo ln -s /usr/bin/make /usr/bin/gmake # 解决 sh: gmake: command not found

1.3 luasocket库的安装

1
2
3
4
5
$ luarocks install luasocket
$ lua
> socket = require("socket")
> print(socket._VERSION)
LuaSocket 2.0.2

1.4 lua-cjson库的安装

1
2
3
4
5
$ luarocks install lua-cjson
$ lua
> cjson = require("cjson")
> print(cjson.encode({ name = "linguofeng" }))
{"name":"linguofeng"}

二、HelloWorld

1
2
3
$ lua
> print("Hello World!!")
Hello World!!

三、数据类型 有8种基本数据类型

  • nil全局变量默认值,如果要删除一个全局变量可以赋值为nil
  • boolean布尔值
  • number数字型
  • string字符串型
  • userdata用户自定义类型,一般是C/C++中的类型
  • function函数
  • thread线程
  • table表
1
2
3
4
5
6
print(type(nil))                    -- 输出 nil
print(type(99.7+12*9)) -- 输出 number
print(type(true)) -- 输出 boolean
print(type("Hello Wikipedia")) -- 输出 string
print(type(print)) -- 输出 function
print(type{1, 2, test = "test"}) -- 输出 table

四、函数 第一类值

函数

第一类值指:在Lua中函数和其他值(数值、字符串)一样,函数可以被存放在变量中,也可以存放在表中,可以作为函数的参数,还可以作为函数的返回值。

1
2
3
4
5
6
7
8
9
10
11
12
13
function add(x, y)             -- 定义一个函数add,并接收两个参数
local a = x + y -- 定义一个局部变量a,接收x+y的和,局部变量仅在函数add中有效
return a -- 返回
end -- 结束add函数

print("15 + 64 = " .. add(15, 64)); -- 打印add(15, 64)的结果

local x = 1 -- local 关键字表示该变量为局部变量,作用域为当前上下文
-- 无该关键字修饰时为全局变量,作用于整个Lua状态机

local add = function(x, y) -- 局部函数,作用于当前脚本(chumk)
Lib = {}
Lib.add = function(x, y) -- 表函数,作用于Lib表

闭包

闭包是一个内部函数以及它的upvalues,内部函数使用了外部(父函数)局部变量。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function newCounter()
local i = 0 -- i为匿名函数的外部局部变量(upvalue)
return function() -- 匿名内部函数
i = i + 1 -- 使用了i,所以该匿名函数是一个闭包
return i
end
end

c1 = newCounter() -- 得到一个匿名函数的变量(闭包)
print(c1()) -- 调用匿名函数,打印出1
print(c1()) -- 调用匿名函数,打印出2

c2 = newCounter()
print(c2()) --> 1
print(c1()) --> 3
print(c2()) --> 2

五、控制语句

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
for int i = 0, 10, 2 do     -- for循环,2表示步长,省略时为1
print("i = " .. i) -- .. 表示字符串连接符
end -- 结束for

if a > b then -- if条件判断语句
print("a > b")
else
print("b > a")
end

while a > b do -- while循环
print("")
end

repeat -- repeat-until循环
print("")
until a > b

六、逻辑运算符 and、or、not

逻辑运算符认为false和nil是假(false),其他为真,0也是true.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
a and b       -- 如果a为false,则返回a,否则返回b
a or b -- 如果a为true,则返回a,否则返回b

x = x or v -- 如果x为false或者nil时则给x赋初始值v

-- 等价于
if not x then
x = v
end

-- 三元运算符
a ? b : c => a and b or c -- and 的优先级别比 or 高

not -- not 的结果只返回false或true,作用类似于"非" "!"取反的意思
print(not nil) -- true
print(not false) -- true
print(not 0) -- false

七、协同程序 coroutine

7.1 创建协同

1
2
3
4
5
co = coroutine.create(function ()   -- 创建一个协同函数,接收一个匿名函数,返回thread类型
print("hi")
end)

print(co) -- thread: 0x7fe1834127d0

7.2 协同的三个状态:挂起态(suspended)、运行态(running)、停止态(dead)。

1
2
3
4
5
print(coroutine.status(co))         -- 查看协同的状态,默认状态是挂起态 suspended

coroutine.resume(co) -- 改变协同的状态为运行太 hi

print(coroutine.status(co)) -- 协同运行完以后将变量停止态 dead

7.3 如此挂起正在运行的协同

1
2
3
4
5
6
7
8
9
co = coroutine.create(function ()
print("hi")
coroutine.yield() -- 协同运行到此状态将变成挂起
print("你好")
end)

coroutine.resume(co) -- hi
coroutine.resume(co) -- 你好
coroutine.resume(co) -- false,协同结束后将不能再使用

7.4 协同数据交换

1
2
3
4
5
6
7
8
9
10
co = coroutine.create(function (x, y)   -- 接收两个参数
print("hi", coroutine.yield(x + y)) -- 返回一个值,同时参数也传递给了coroutine.yield
return 100 -- 第三种返回值的方式
end)

print(coroutine.resume(co, 12, 87)) -- 传递两个参数并接收返回值(true, 99)

-- 执行coroutine.yield(x + y)之前协同被挂起,但值被返回,因此print函数未被执行,下面执行

print(coroutine.resume(co, 12, 87)) -- 传递两个参数并接收返回值(true, 100)

八、数据结构 table

8.1 表的创建

1
2
3
4
5
6
7
8
9
arrays = {}                         -- 创建一个空表
arrays[1] = "abc" -- 第一个索引值为1
arrays[2] = 123

arrays["key"] = "value" -- map

for key, value in pairs(arrays) do -- 迭代table
print(key .. " = " .. value)
end

8.2 表的增删改查

1
2
3
4
5
6
7
8
9
10
11
list = {123}            -- 初始化表
list[2] = "abc" -- 增
list.x = 123
list.y = 987
list[1] = nil -- 删
list.y = nil
list[2] = 456 -- 改
list.x = 987
print(list[2]) -- 查
print(list.x)
print(list['x'])

8.3 数组

1
2
3
4
list = {}               -- 初始空化数组,数组的下标是整数,遵循Lua的标准,下标从1开始
list[1] = "abc"
list[2] = "edg"
list[3] = "hij"

8.4 矩阵(二维数组)

1
2
3
4
5
6
7
mt = {}                 -- 创建矩阵matrix
for i = 1, N do -- 创建N行
mt[i] = {} -- 每行都是一个数组(table元素)
for j = 1, M do -- 创建M列
mt[i][j] = "a" -- 第N行第M行的值
end
end

8.5 链表

1
2
3
4
5
6
7
8
9
10
11
12
13
Singly-linked-list.svg

list = nil
list = {next = list, value = "hello3"}
list = {next = list, value = "hello2"}
list = {next = list, value = "hello1"}

-- 遍历
local l = list
while l do
print(l.value)
l = l.next
end

九、metatable 元表

9.1 元表与元方法

元表也是普通表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
t = {}
print(getmetatable(t)) -- 获取表的metatable nil,默认不带

mt = {}
setmetatable(t, mt) -- 设置一个元素

-- metamethod 元表的方法(元方法)
mt.__add -- 加 +
mt.__sub -- 减 -
mt.__mul -- 乘 *
mt.__div -- 除 /
mt.__unm -- 负 -
mt.__pow -- 幂 ^
mt.__concat -- 连接

mt.__eq -- 等于 =
mt.__lt -- 小于 <
mt.__le -- 大于 >

mt.__tostring -- print调用
mt.__metatable -- 设置该元表不被修改与访问

mt.__index -- 当访问不存在的元素时会去查询,相当于子类继承父类一样
mt.__newindex -- 更新表,如果增加一个不存在的元素,会去查询,有直接用,否则增加

9.2 表的代理

记录下表的增查记录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
local index = {}                  -- 私有的key,用来记录原始表在代理表中的下标
local mt = { -- 创建元表
__index = function(t, k)
print("访问了" .. tostring(k) .. "元素")
return t[index][k] -- 从代理表中获取原始表中k下标的数据
end,

__newindex = function(t, k, v)
print("更新了 " .. tostring(k) .. " 元素的值为 " .. tostring(v))
t[index][k] = v -- 更新代理表中下标为index的原始表中的元素
end
}

function setProxy(t)
local proxy = {} -- 创建代理表
proxy[index] = t -- 把原始表加到代理表的index下标中
setmetatable(proxy, mt) -- 设置代理表的元表
return proxy -- 返回代理表,即所有操作都是直接操作代理表
end

p = setProxy({})

p[2] = 'abcdefg' -- 更新了 2 元素的值为 abcdefg
print(p[2]) -- 访问了2元素

十、环境

10.1 全局变量 _G

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
> _G["ewgegw"] = "ddddddgege"
> table.foreach(_G, print)
string table: 0x7ffce3407a60
xpcall function: 0x7ffce3404780
package table: 0x7ffce3405780
tostring function: 0x7ffce3405020
print function: 0x7ffce3405160
os table: 0x7ffce34073e0
unpack function: 0x7ffce34050d0
ewgegw ddddddgege -- 上面添加的全局变量
require function: 0x7ffce3405e70
getfenv function: 0x7ffce3404db0
setmetatable function: 0x7ffce3404f60
next function: 0x7ffce3404d20
assert function: 0x7ffce3404a80
tonumber function: 0x7ffce3404fc0
io table: 0x7ffce3406bd0
rawequal function: 0x7ffce34051b0
collectgarbage function: 0x7ffce3404ad0
getmetatable function: 0x7ffce3404e00
module function: 0x7ffce3405e20
rawset function: 0x7ffce3405260
math table: 0x7ffce3408290
debug table: 0x7ffce3408c50
pcall function: 0x7ffce3404d70
table table: 0x7ffce3405f10
newproxy function: 0x7ffce34052e0
type function: 0x7ffce3405080
coroutine table: 0x7ffce3405380 -- 对应的是协同的表
_G table: 0x7ffce3404110
select function: 0x7ffce3404ec0
gcinfo function: 0x7ffce3404150
pairs function: 0x7ffce34048c0
rawget function: 0x7ffce3405210
loadstring function: 0x7ffce3404cc0
ipairs function: 0x7ffce3404830
_VERSION Lua 5.1
dofile function: 0x7ffce3404bd0
setfenv function: 0x7ffce3404f10
load function: 0x7ffce3404c70
error function: 0x7ffce3404c20
loadfile function: 0x7ffce3404e60

> table.foreach(_G.os, print)

10.2 非全局变量 setfenv

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
--pack.lua
local P = {}

-- 改变P表的__index,这里的_G代表全局环境
setmetatable(P, {__index = _G})

-- 改变当前的环境为P,setfenv前的所有定义都是在全局环境中进行的,后面的则都是在新环境中进行的,互不影响
setfenv(1, P)
文章作者: 安全书
文章链接: https://lua.ren/zl/2016-01-01-451_Lua/
版权声明: 本博客所有文章除特别声明外,均采用 null 许可协议。转载请注明来自 安全书