Lua 是一种轻量小巧的脚本语言,用标准C语言编写并以源代码形式开放, 其设计目的是为了嵌入应用程序中,从而为应用程序提供灵活的扩展和定制功能。
Lua 是巴西里约热内卢天主教大学(Pontifical Catholic University of Rio de Janeiro)里的一个研究小组,由Roberto Ierusalimschy、Waldemar Celes 和 Luiz Henrique de Figueiredo所组成并于1993年开发。

Lua 应用场景

游戏开发
独立应用脚本
Web 应用脚本
扩展和数据库插件如:MySQL Proxy 和 MySQL WorkBench
安全系统,如入侵检测系统

Hello world!

1
print("Hello World")

注释

1
2
3
4
--行注释
--[[
块注释
--]]

变量

全局变量

默认即为全局变量,无需声明符号 访问未定义的全局变量不出错
*删除全局变量将其赋值为nil(等同于其他语言NULL)即可

1
2
3
4
5
a=10 --全局变量
print(a) --10
a=nil --删除该变量
print(a) --nil

局部变量

  • 局部变量的作用域为从声明位置开始到所在语句块结束(或者是直到下一个同名局部变量的声明)。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    local data = 100;
    local function fun1()
    print(data);
    data = data+50;
    end
    data = 200;
    local data = 300; -- 重新定义一个局部变量
    local function fun2()
    print(data);
    data = data+50;
    end
    data = 400;
    --调用
    fun1(); -- 200
    fun2(); -- 400
    fun1(); -- 250
    fun2(); -- 450

赋值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
a, b = 10, 2*x
x, y = y, x -- swap 'x' for 'y'
a[i], a[j] = a[j], a[i] -- swap 'a[i]' for 'a[i]'
-[[赋值多个的时候,如下规则
a. 变量个数 > 值的个数 按变量个数补足nil
b. 变量个数 < 值的个数 多余的值会被忽略
]]
a, b, c = 0, 1
print(a,b,c) --> 0 1 nil
a, b = a+1, b+1, b+2 -- value of b+2 is ignored
print(a,b) --> 1 2
a, b, c = 0 --> 易错!注意!!!
print(a,b,c) --> 0 nil nil

索引

1
2
3
t[i]
t.i -- 当索引为字符串类型时的一种简化写法
gettable_event(t,i) -- 采用索引访问本质上是一个类似这样的函数调用

数据类型

类型 说明
nil 这个最简单,只有值nil属于该类,表示一个无效值(在条件表达式中相当于false)。
boolean 包含两个值:false和true。
number 表示双精度类型的实浮点数
string 字符串由一对双引号或单引号来表示
function 由 C 或 Lua 编写的函数
userdata 表示任意存储在变量中的C数据结构
thread 表示执行的独立线路,用于执行协同程序
table Lua 中的表(table)其实是一个”关联数组”(associative arrays),数组的索引可以是数字或者是字符串。在 Lua 里,table 的创建是通过”构造表达式”来完成,最简单构造表达式是{},用来创建一个空表。
1
2
3
4
5
6
7
8
--可用type查看类型
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

nil

nil 类型表示一种没有任何有效值,它只有一个值 – nil,例如打印一个没有赋值的变量,便会输出一个 nil 值:

1
print(type(a)) -- nil

对于全局变量和 table,nil 还有一个”删除”作用,给全局变量或者 table 表里的变量赋一个 nil 值,等同于把它们删掉,执行下面代码就知:

1
2
3
4
5
6
7
8
9
tab1 = { key1 = "val1", key2 = "val2", "val3" }
for k, v in pairs(tab1) do
print(k .. " - " .. v)
end
tab1.key1 = nil
for k, v in pairs(tab1) do
print(k .. " - " .. v) --不会打印key1了
end

bool类型

  • boolean 类型只有两个可选值:true(真) 和 false(假),Lua 把 false 和 nil 看作是”假”,其他的都为”真”:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    print(type(true))
    print(type(false))
    print(type(nil))
    if false or nil then
    print("至少有一个是 true")
    else
    print("false 和 nil 都为 false!") --false 和 nil 都为 false!
    end

number

  • Lua 默认只有一种 number 类型 – double(双精度)类型(默认类型可以修改 luaconf.h 里的定义),
    1
    2
    3
    4
    5
    6
    print(type(2))
    print(type(2.2))
    print(type(0.2))
    print(type(2e+1))
    print(type(0.2e-1))
    print(type(7.8263692594256e-06))

string

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
string1 = "this is string1"
--[[可以输出换行的string]]
html = [[
<html>
<head></head>
<body>
<a href="http://www.w3cschool.cc/">w3cschool菜鸟教程</a>
</body>
</html>
]]
print(html)
--算数操作Lua尝试转换为number进行操作
print("1"+2) --3.0
--字符串连接用..
print("1".."2") --12
--符号#可计算字符串长度
print(#html)

table(表)

  • 在 Lua 里,table (类似于map,将list与map结合起来)的创建是通过”构造表达式”来完成,最简单构造表达式是{},用来创建一个空表。也可以在表里添加一些数据,直接初始化表:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
-- 创建一个空的 table
local tbl1 = {}
-- 直接初始表 index由1开始
local tbl2 = {"apple", "pear", "orange", "grape"}
for key, val in pairs(tbl2) do
print("Key", key, val)
end
a = {}
a["key"] = "value" --保存一个键值对 "key":"value"
key = 10
a[key] = 22 --10:22
a[key] = a[key] + 11
for k, v in pairs(a) do --两个键值对
print(k .. " : " .. v)
end
  • table 不会固定长度大小,有新数据添加时 table 长度会自动增长,没初始的 table 都是 nil。
    1
    2
    3
    4
    5
    6
    7
    a3 = {}
    for i = 1, 10 do
    a3[i] = i
    end
    a3["key"] = "val"
    print(a3["key"]) --val
    print(a3["none"]) --nil

function(函数)

1
2
3
4
5
6
7
8
9
10
11
function factorial1(n)
if n == 0 then
return 1
else
return n * factorial1(n - 1)
end
end
print(factorial1(5))
factorial2 = factorial1
print(factorial2(5))

*function 可以以匿名函数(anonymous function)的方式通过参数传递:

1
2
3
4
5
6
7
8
9
function anonymous(tab, fun)
for k, v in pairs(tab) do
print(fun(k, v))
end
end
tab = { key1 = "val1", key2 = "val2" }
anonymous(tab, function(key, val)
return key .. " = " .. val
end)

thread(线程)

在 Lua 里,最主要的线程是协同程序(coroutine)。它跟线程(thread)差不多,拥有自己独立的栈、局部变量和指令指针,可以跟其他协同程序共享全局变量和其他大部分东西。
线程跟协程的区别:线程可以同时多个运行,而协程任意时刻只能运行一个,并且处于运行状态的协程只有被挂起(suspend)时才会暂停。

userdata(自定义类型)

userdata 是一种用户自定义数据,用于表示一种由应用程序或 C/C++ 语言库所创建的类型,可以将任意 C/C++ 的任意数据类型的数据(通常是 struct 和 指针)存储到 Lua 变量中调用。

循环

while

1
2
3
4
5
6
a=10
while( a < 20 )
do
print("a 的值为:", a)
a = a+1
end

for

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
for var=exp1,exp2,exp3 do
<执行体>
end
--var从exp1变化到exp2,每次变化以exp3为步长递增var,并执行一次"执行体"。exp3是可选的,如果不指定,默认为1。
--eg.
for i=1,f(x) do --此处的f(x)只会在初始化循环时候执行一次
print(i)
end
for i=10,1,-1 do
print(i)
end
--f(x)的例子
function f(x)
print("function")
return x*2
end
for i=1,f(5) do print(i)
end
--[[
function
1
2
3
4
5
6
7
8
9
10
--]]

泛型for循环

1
2
3
4
5
6
--打印数组a的所有值
a = {"Lua", "Tutorial"}
for i,v in ipairs(a)
do print(i..v)
end
--i是数组索引值,v是对应索引的数组元素值。ipairs是Lua提供的一个迭代器函数,用来迭代数组。

repeat…until(类似do while)

1
2
3
4
5
6
7
--[ 变量定义 --]
a = 10
--[ 执行循环 --]
repeat
print("a的值为:", a)
a = a + 1
until( a > 15 )

不同循环可以嵌套

1
2
3
4
5
6
7
8
9
10
11
12
j =2
for i=2,10 do
for j=2,(i/j) , 2 do
if(not(i%j))
then
break
end
if(j > (i/j))then
print("i 的值为:",i)
end
end
end

break

1
2
3
4
5
6
7
8
9
10
11
12
13
14
--[ 定义变量 --]
a = 10
--[ while 循环 --]
while( a < 20 )
do
print("a 的值为:", a)
a=a+1
if( a > 15)
then
--[ 使用 break 语句终止循环 --]
break
end
end

流程控制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
--[ 定义变量 --]
a = 100
--[ 检查布尔条件 --]
if( a == 10 )
then
--[ 如果条件为 true 打印以下信息 --]
print("a 的值为 10" )
else if( a == 20 )
then
--[ if else if 条件为 true 时打印以下信息 --]
print("a 的值为 20" )
else if( a == 30 )
then
--[ if else if condition 条件为 true 时打印以下信息 --]
print("a 的值为 30" )
else
--[ 以上条件语句没有一个为 true 时打印以下信息 --]
print("没有匹配 a 的值" )
end
print("a 的真实值为: ", a )

函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
--实例
--[[ 函数返回两个值的最大值 --]]
function max(num1, num2)
if (num1 > num2) then
result = num1;
else
result = num2;
end
return result;
end
-- 调用函数
print("两值比较最大值为 ",max(10,4))
print("两值比较最大值为 ",max(5,6))

函数作为参数

1
2
3
4
5
6
7
8
9
10
11
12
myprint = function(param)
print("这是打印函数 - ##",param,"##")
end
function add(num1,num2,functionPrint)
result = num1 + num2
-- 调用传递的函数参数
functionPrint(result)
end
myprint(10)
-- myprint 函数作为参数传递
add(2,5,myprint)

多返回值

1
s, e = string.find("w3cschool菜鸟教程:www.w3cschool.cc", "菜鸟教程") print(s,e)
1
2
3
4
5
6
7
8
9
10
11
12
13
function maximum (a)
local mi = 1 -- 最大值索引
local m = a[mi] -- 最大值
for i,val in ipairs(a) do
if val > m then
mi = i
m = val
end
end
return m, mi
end
print(maximum({8,10,23,12,5}))

可变参数

1
2
3
4
5
6
7
8
9
10
11
12
function average(...)
result = 0
local arg={...}
for i,v in ipairs(arg) do
result = result + v
end
--#arg 表示传入参数的个数
print("总共传入 " .. #arg .. " 个数")
return result/#arg
end
print("平均值为",average(10,5,3,4,5,6))

运算符

算数

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
a = 21
b = 10
c = a + b
print("Line 1 - c 的值为 ", c )
c = a - b
print("Line 2 - c 的值为 ", c )
c = a * b
print("Line 3 - c 的值为 ", c )
c = a / b
print("Line 4 - c 的值为 ", c )
c = a % b
print("Line 5 - c 的值为 ", c )
c = a^2
print("Line 6 - c 的值为 ", c )
c = -a
print("Line 7 - c 的值为 ", c )
--[[
Line 1 - c 的值为 31
Line 2 - c 的值为 11
Line 3 - c 的值为 210
Line 4 - c 的值为 2.1
Line 5 - c 的值为 1
Line 6 - c 的值为 441
Line 7 - c 的值为 -21
--]]

关系

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
a = 21
b = 10
if( a == b )
then
print("Line 1 - a 等于 b" )
else
print("Line 1 - a 不等于 b" )
end
if( a ~= b )
then
print("Line 2 - a 不等于 b" )
else
print("Line 2 - a 等于 b" )
end
if ( a < b )
then
print("Line 3 - a 小于 b" )
else
print("Line 3 - a 大于等于 b" )
end
if ( a > b )
then
print("Line 4 - a 大于 b" )
else
print("Line 5 - a 小于等于 b" )
end
-- 修改 a 和 b 的值
a = 5
b = 20
if ( a <= b )
then
print("Line 5 - a 小于等于 b" )
end
if ( b >= a )
then
print("Line 6 - b 大于等于 a" )
end

逻辑

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
a = true
b = true
if ( a and b )
then
print("a and b - 条件为 true" )
end
if ( a or b )
then
print("a or b - 条件为 true" )
end
print("---------分割线---------" )
-- 修改 a 和 b 的值
a = false
b = true
if ( a and b )
then
print("a and b - 条件为 true" )
else
print("a and b - 条件为 false" )
end
if ( not( a and b) )
then
print("not( a and b) - 条件为 true" )
else
print("not( a and b) - 条件为 false" )
end

特殊

1
2
3
4
5
6
7
8
9
10
a = "Hello "
b = "World"
print("连接字符串 a 和 b ", a..b )
print("b 字符串长度 ",#b )
print("字符串 Test 长度 ",#"Test" )
print("w3cschool菜鸟教程网址长度 ",#"www.w3cschool.cc" )

字符串

示例

1
2
3
4
5
6
7
string1 = "Lua"
print("\"字符串 1 是\"",string1)
string2 = 'w3cschool.cc'
print("字符串 2 是",string2)
string3 = [["Lua 教程"]]
print("字符串 3 是",string3)

字符串常用操作

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
--转大写
print(string.upper("abc"))
--转小写
print(string.upper("ABC"))
--替换字符串 数字表示替换次数,忽略则全部替换
print(string.gsub("abcabc",'c','d',1))
--字符串查找 数字为起始位置,找到则返回起止位置
print(string.find("abcabc",'a',3))
--字符串反转
print(string.reverse("12345"))
--格式化字符串
string.format("the value is:%d",4)
--数字转换为字符串并连接
print(string.char(97,98,99,100))
--byte转换为整数,可指定位置,默认为1
print(string.byte("abc",2))
--计算长度 同#"string"
print(string.len("abc")==#"abc")
--返回字符串的n个拷贝
print(string.rep('a', 3))
--连接字符串
print("aaa"..'bbb')

数组

一维

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
array = {"Lua", "Tutorial"}
--Lua 索引值是以 1 为起始,但你也可以指定 0 开始。
for i= 0, 2 do
print(array[i])
end
--也可以用负数作为索引
array = {}
for i= -2, 2 do
array[i] = i *2
end
for i = -2,2 do
print(array[i])
end

多维

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
-- 初始化数组
array = {}
for i=1,3 do
array[i] = {}
for j=1,3 do
array[i][j] = i*j
end
end
-- 访问数组
for i=1,3 do
for j=1,3 do
print(array[i][j])
end
end

迭代器

内置简单迭代

1
2
3
4
5
6
array = {"Lua", "Tutorial"}
for key,value in ipairs(array)
do
print(key, value)
end

自定义的迭代器

1
2
3
4
5
6
7
8
9
10
11
12
function square(iteratorMaxCount,currentNumber)
if currentNumber<iteratorMaxCount
then
currentNumber = currentNumber+1
return currentNumber, currentNumber*currentNumber
end
end
for i,n in square,4,0
do
print(i,n)
end

利用闭包实现的迭代器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
array = {"Lua", "Tutorial"}
function elementIterator (collection)
local index = 0
local count = #collection
-- 闭包函数
return function ()
index = index + 1
if index <= count
then
-- 返回迭代器的当前元素
return collection[index]
end
end
end
for element in elementIterator(array)
do
print(element)
end

Lua table

  • table 是 Lua 的一种数据结构用来帮助我们创建不同的数据类型,如:数字、字典等。
  • Lua table 使用关联型数组,你可以用任意类型的值来作数组的索引,但这个值不能是 nil。
  • Lua table 是不固定大小的,你可以根据自己需要进行扩容。
  • Lua也是通过table来解决模块(module)、包(package)和对象(Object)的。 例如string.format表示使用”format”来索引table string。

初始化与销毁

1
2
3
4
5
6
7
8
9
-- 初始化表
mytable = {}
-- 指定值
mytable[1]= "Lua"
-- 移除引用
mytable = nil
-- lua 垃圾回收会释放内存

table的引用(类似Java)

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
-- 简单的 table 演示table类型的引用类似Java
mytable = {}
print("mytable 的类型是 ",type(mytable))
mytable[1]= "Lua"
mytable["wow"] = "修改前"
print("mytable 索引为 1 的元素是 ", mytable[1])
print("mytable 索引为 wow 的元素是 ", mytable["wow"])
-- alternatetable和mytable的是指同一个 table
alternatetable = mytable
print("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)

table操作

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
--连接table中的所有元素,利用分隔符
t1={"a","b","c"}
print(table.concat(t1))
print(table.concat(t1,"@@"))
print(table.concat(t1,"@@",1,2))
--插入元素
table.insert(t1,"d")
print(t1[4])
table.insert(t1,2,"b")
print(t1[2])
--删除元素
print("当前元素:"..table.concat(t1,","))
table.remove(t1)
print("remove(t1)默认删除最后一个元素,当前元素:"..table.concat(t1,","))
table.remove(t1,1)
print("remove(t1,1)之后"..table.concat(t1,","))
--排序
t1={"c","b","a"}
print("排序前:"..table.concat(t1,","))
table.sort(t1)
print("排序后:"..table.concat(t1,","))

!!!慎用”#table”

  • 不要赋值nil,删除元素请使用remove
  • 元素不是同一类型length无明确意义
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    tbl = {"a", "b","c"}
    print("tbl 长度 ", #tbl) --3
    tbl = {"a", "b","c"="bb"}
    print("tbl 长度 ", #tbl) --2
    tbl = {"a", "b",nil}
    print("tbl 长度 ", #tbl) --2
    tbl = {"a", nil,"b",nil} --1
    print("tbl 长度 ", #tbl)

模块与包

自定义包

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
-- 文件名为 module.lua
-- 定义一个名为 module 的模块
module = {}
-- 定义一个常量
module.constant = "这是一个常量"
-- 定义一个函数
function module.func1()
io.write("这是一个公有函数!\n")
end
local function func2()
print("这是一个私有函数!")
end
function module.func3()
func2()
end
return module
1
2
3
4
5
6
7
8
9
10
11
12
--使用自定义包
require("module")
print(module.constant)
module.func3()
--设置别名
local m = require("module")
print(m.constant)
m.func3()

加载策略

  • require 用于搜索 Lua 文件的路径是存放在全局变量 package.path 中,当 Lua 启动后,会以环境变量LUA_PATH的值来初始这个环境变量。如果没有找到该环境变量,则使用一个编译时定义的默认路径来初始化。
    1
    2
    #LUA_PATH
    export LUA_PATH="~/lua/?.lua;;"

元表

  • 元表就是在table上加上自定义的对表数据的操作

    __index

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    --__index元方法查看表中元素是否存在,如果不存在,返回结果为nil;如果存在则由__index 返回结果。
    mytable = setmetatable({key1 = "value1"}, {
    __index = function(mytable, key)
    if key == "key2" then
    return "metatablevalue"
    else
    return mytable[key]
    end
    end
    })
    print(mytable.key1,mytable.key2)

__newindex

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
--当你给表的一个缺少的索引赋值,解释器就会查找__newindex元方法:如果存在则调用这个函数而不进行赋值操作。
mymetatable = {}
mytable = setmetatable({key1 = "value1"}, { __newindex = mymetatable })
print(mytable.key1)
mytable.newkey = "新值2"
print(mytable.newkey,mymetatable.newkey)
mytable.key1 = "新值1"
print(mytable.key1,mymetatable.key1)
mytable = setmetatable({key1 = "value1"}, {
__newindex = function(mytable, key, value)
rawset(mytable, key, "\""..value.."\"")
end
})
--使用rawset 函数来更新表
mytable.key1 = "new value"
mytable.key2 = 4
print(mytable.key1,mytable.key2)

__add

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
--为表添加操作符
-- 自定义计算表中最大值函数 table_maxn
function table_maxn(t)
local mn = 0
for k, v in pairs(t) do
if mn < k then
mn = k
end
end
return mn
end
-- 两表相加操作(合并)
mytable = setmetatable({ 1, 2, 3 }, {
__add = function(mytable, newtable)
for i = 1, table_maxn(newtable) do
table.insert(mytable, table_maxn(mytable)+1,newtable[i])
end
return mytable
end
})
secondtable = {4,5,6}
mytable = mytable + secondtable
for k,v in ipairs(mytable) do
print("index:"..k,v)
end
--[[
__add 对应的运算符 '+'.
__sub 对应的运算符 '-'.
__mul 对应的运算符 '*'.
__div 对应的运算符 '/'.
__mod 对应的运算符 '%'.
__unm 对应的运算符 '-'.
__concat 对应的运算符 '..'.
__eq 对应的运算符 '=='.
__lt 对应的运算符 '<'.
__le 对应的运算符 '<='.
--]]

__call

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
--__call元方法
-- 计算表中最大值,table.maxn在Lua5.2以上版本中已无法使用
-- 自定义计算表中最大值函数 table_maxn
function table_maxn(t)
local mn = 0
for k, v in pairs(t) do
if mn < k then
mn = k
end
end
return mn
end
-- 定义元方法__call
mytable = setmetatable({10}, {
__call = function(mytable, newtable)
sum = 0
for i = 1, table_maxn(mytable) do
sum = sum + mytable[i]
end
for i = 1, table_maxn(newtable) do
sum = sum + newtable[i]
end
return sum
end
})
newtable = {10,20,30}
print(mytable(newtable))

__tostring

1
2
3
4
5
6
7
8
9
10
11
--__tostring 元方法用于修改表的输出行为
mytable = setmetatable({ 10, 20, 30 }, {
__tostring = function(mytable)
sum = 0
for k, v in pairs(mytable) do
sum = sum + v
end
return "表所有元素的和为 " .. sum
end
})
print(mytable)

协同(coroutine)

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
46
47
48
49
50
51
--协程
-- coroutine_test.lua 文件
--create创建一个协程,未调用之前为阻塞状态
--此时co是协程对象
co = coroutine.create(
function(i)
print(i);
end
)
print(coroutine.status(co)) -- suspended
--与create配合使用唤醒协程进行传参
coroutine.resume(co, 1) -- 1
print(coroutine.status(co)) -- dead
print("----------")
--wrap返回一个函数a,调用a()则进入协程并传递参数
--此时co表示一个函数
co = coroutine.wrap(
function(i)
print(i);
end
)
co(1)
print("----------")
co2 = coroutine.create(
function()
for i=1,10 do
print(i)
if i == 3 then
print(coroutine.status(co2)) --running
print(coroutine.running()) --返回正在运行的线程号 thread:XXXXXX
end
coroutine.yield()--挂起coroutine,将coroutine设置为挂起状态,这个和resume配合使用能有很多有用的效果
end
end
)
coroutine.resume(co2) --1
coroutine.resume(co2) --2
coroutine.resume(co2) --3
print(coroutine.status(co2)) -- suspended
print(coroutine.running())
print("----------")

resume与create的配合 注意resume与yield的参数传递

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
function foo (a)
print("foo 函数输出", a)
return coroutine.yield(2 * a) -- 返回 2*a 的值
end
co = coroutine.create(function (a , b)
print("第一次协同程序执行输出", a, b) -- co-body 1 10
local r = foo(a + 1)
print("第二次协同程序执行输出", r)
local r, s = coroutine.yield(a + b, a - b) -- a,b的值为第一次调用协同程序时传入
print("第三次协同程序执行输出", r, s)
return b, "结束协同程序" -- b的值为第二次调用协同程序时传入
end)
--首次启动协同程序,参数传递给函数
print("main", coroutine.resume(co, 1, 10)) -- true, 4
--第二次唤醒协同程序,参数传递给上一次的yield() line:3
print("main", coroutine.resume(co, "r")) -- true 11 -9
--第三次唤醒协同程序,参数传递给上一次的yield() line:11
print("main", coroutine.resume(co, "x", "y")) -- true 10 end
print("main", coroutine.resume(co, "x", "y")) -- cannot resume dead coroutine

生产者-消费者

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
local newProductor
function productor()
local i = 0
while true do
i = i + 1
send(i) -- 将生产的物品发送给消费者
end
end
function consumer()
while true do
local i = receive() -- 从生产者那里得到物品
print(i)
end
end
function receive()
local status, value = coroutine.resume(newProductor)
return value
end
function send(x)
coroutine.yield(x) -- x表示需要发送的值,值返回以后,就挂起该协同程序
end
-- 启动程序
newProductor = coroutine.create(productor)
consumer()

IO

简单模式(类c语言)

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
46
--lua文件IO简单模式 类似c语言
--[[
file = io.open (filename [, mode])
r 以只读方式打开文件,该文件必须存在。
w 打开只写文件,若文件存在则文件长度清为0,即该文件内容会消失。若文件不存在则建立该文件。
a 以附加的方式打开只写文件。若文件不存在,则会建立该文件,如果文件存在,写入的数据会被加到文件尾,即文件原先的内容会被保留。(EOF符保留)
r+ 以可读写方式打开文件,该文件必须存在。
w+ 打开可读写文件,若文件存在则文件长度清为零,即该文件内容会消失。若文件不存在则建立该文件。
a+ 与a类似,但此文件可读可写
b 二进制模式,如果文件是二进制文件,可以加上b
+ 号表示对文件既可以读也可以写
--]]
--
-- 以只读方式打开文件
file = io.open("test.txt", "r")
-- 设置默认输入文件为 test.lua
io.input(file)
-- 读取一个数字
print(io.read("*n"))
-- 读取n个字符
print(io.read(2))
-- 输出文件下一行 默认可不写,当前位置读取至换行止
print(io.read("*l"))
-- 从当前位置读取整个文件
print(io.read("*a"))
-- 关闭打开的文件
io.close(file)
-- 以附加的方式打开只写文件
file = io.open("test.lua", "a")
-- 设置默认输出文件为 test.lua
io.output(file)
-- 在文件最后一行添加 Lua 注释
io.write("-- test.lua 文件末尾注释")
-- 关闭打开的文件
io.close(file)

其他的IO函数

  • io.tmpfile():返回一个临时文件句柄,该文件以更新模式打开,程序结束时自动删除
  • io.type(file): 检测obj是否一个可用的文件句柄
  • io.flush(): 向文件写入缓冲中的所有数据
  • io.lines(optional file name):返回一个迭代函数,每次调用将获得文件中的一行内容,当到文件尾时,将返回nil,但不关闭文件

完全模式 IO.xxx变为file.xxx

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
-- 以只读方式打开文件
file = io.open("test.lua", "r")
-- 输出文件第一行
print(file:read())
-- 关闭打开的文件
file:close()
-- 以附加的方式打开只写文件
file = io.open("test.lua", "a")
-- 在文件最后一行添加 Lua 注释
file:write("--test")
-- 关闭打开的文件
file:close()
  • file:seek(optional whence, optional offset) 设置和获取当前文件位置
  • file:flush(): 向文件写入缓冲中的所有数据
  • io.lines(optional file name)打开指定的文件filename为读模式并返回一个迭代函数,每次调用将获得文件中的一行内容,当到文件尾时,将返回nil,并自动关闭文件。
    1
    2
    3
    4
    5
    6
    7
    8
    -- 以只读方式打开文件
    file = io.open("test.lua", "r")
    -- 从倒数25位置开始读取整个文件内容
    file:seek("end",-25)
    print(file:read("*a"))
    -- 关闭打开的文件
    file:close()
1
2
3
4
5
for line in io.lines("test.lua") do
print(line)
end

错误处理

assert与error

1
2
3
4
5
6
7
8
9
10
11
12
13
local function add(a,b)
assert(type(a) == "number", "a is not a number")
assert(type(b) == "number", "b is not a number")
if(a==b) then
-- error终止正在执行的函数,返回message,第二个参数为0,1,2
-- 表示0:不添加错误位置信息;1(默认):调用error位置(文件+行号)
-- 2:指出哪个调用error的函数的函数
error("err",2)
end
return a+b
end
print(add(10,20))

pcall与xpcall

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
--pcall表示保护模式运行程序,第一个参数为函数,第二个参数为函数的参数
pcall(function(i) print(i) end, aa==1)
--无错误返回true,有错误返回false,err_info
has_err,err_info=pcall(function(i) print(i) error('error..') end, 33)
print(has_err,err_info)
--xpcall提供更详细的信息
function myfunction ()
n = n/nil
end
function myerrorhandler( err )
print( "ERROR:", err )
end
status = xpcall( myfunction, myerrorhandler )
print( status)

垃圾回收 lua为自动垃圾回收

  • collectgarbage(“collect”): 做一次完整的垃圾收集循环。通过参数 opt 它提供了一组不同的功能:
  • collectgarbage(“count”): 以 K 字节数为单位返回 Lua 使用的总内存数。 这个值有小数部分,所以只需要乘上 1024 就能得到 Lua 使用的准确字节数(除非溢出)。
  • collectgarbage(“restart”): 重启垃圾收集器的自动运行。
  • collectgarbage(“setpause”): 将 arg 设为收集器的 间歇率 (参见 §2.5)。 返回 间歇率 的前一个值。
  • collectgarbage(“setstepmul”): 返回 步进倍率 的前一个值。
  • collectgarbage(“step”): 单步运行垃圾收集器。 步长”大小”由 arg 控制。 传入 0 时,收集器步进(不可分割的)一步。 传入非 0 值, 收集器收集相当于 Lua 分配这些多(K 字节)内存的工作。 如果收集器结束一个循环将返回 true 。
  • collectgarbage(“stop”): 停止垃圾收集器的运行。 在调用重启前,收集器只会因显式的调用运行。

面向对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
-- 面向对象,使用table模拟实现
-- Meta class
Shape = {area = 0}
-- 基础类方法 new
function Shape:new (o,side)
o = o or {}
-- self类似this
setmetatable(o, self)
self.__index = self
side = side or 0
self.area = side*side;
return o
end
-- 基础类方法 printArea
function Shape:printArea ()
print("面积为 ",self.area)
end
-- 创建对象
myshape = Shape:new(nil,10)
myshape:printArea()

继承与方法重写

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
46
47
48
49
50
51
52
53
54
55
56
-- Meta class
Shape = {area = 0}
-- 基础类方法 new
function Shape:new (o,side)
o = o or {}
setmetatable(o, self)
self.__index = self
side = side or 0
self.area = side*side;
return o
end
-- 基础类方法 printArea
function Shape:printArea ()
print("面积为 ",self.area)
end
-- 创建对象
myshape = Shape:new(nil,10)
myshape:printArea()
Square = Shape:new()
-- 派生类方法 new
function Square:new (o,side)
o = o or Shape:new(o,side)
setmetatable(o, self)
self.__index = self
return o
end
-- 派生类方法 printArea 重写
function Square:printArea ()
print("正方形面积为 ",self.area)
end
-- 创建对象
mysquare = Square:new(nil,10)
mysquare:printArea()
Rectangle = Shape:new()
-- 派生类方法 new
function Rectangle:new (o,length,breadth)
o = o or Shape:new(o)
setmetatable(o, self)
self.__index = self
self.area = length * breadth
return o
end
-- 派生类方法 printArea
function Rectangle:printArea ()
print("矩形面积为 ",self.area)
end
-- 创建对象
myrectangle = Rectangle:new(nil,10,20)
myrectangle:printArea()

开始

安装完成后:

1
2
$ git config --global user.name "Your Name"
$ git config --global user.email "email@example.com"

基本

初始化一个Git仓库,使用git init命令。

添加文件到Git仓库,分两步:

  • 第一步,使用命令git add <file>,注意,可反复多次使用,添加多个文件;
  • 第二步,使用命令git commit,完成。
  • 要随时掌握工作区的状态,使用git status命令。
  • 如果git status告诉你有文件被修改过,用git diff可以查看修改内容。
  • HEAD指向的版本就是当前版本HEAD^表示上一个版本(HEAD~1),Git允许我们在版本的历史之间穿梭,使用命令git reset --hard commit_id
  • 穿梭前,用git log可以查看提交历史,以便确定要回退到哪个版本。
  • 要重返未来,用git reflog查看命令历史,以便确定要回到未来的哪个版本。

撤销相关

  • 当你改乱了工作区某个文件的内容,想直接丢弃工作区的修改时,用命令git checkout -- file
  • 当你不但改乱了工作区某个文件的内容,还添加到了暂存区时,想丢弃修改,分两步,第一步用命令git reset HEAD file,就回到了场景1,第二步按场景1操作。
  • 已经提交了不合适的修改到版本库时,则进行版本回退。
  • 删除一个文件git rm,然后commit
  • 当把错误的内容push到远程之后,撤销方式:
1
2
3
4
5
6
7
8
#将指针指向错误内容的前一个commit_id
#--hard参数有另外两种替换项:
#git reset –mixed:(撤销commit,撤销add,代码修改改在)此为默认方式,不带任何参数的git reset,即时这种方式,它回退到某个版本,只保留源码,回退commit和index信息
#git reset –soft:(撤销commit,不撤销add,代码修改改在)回退到某个版本,只回退了commit的信息,不会恢复到index file一级。
#git reset –hard:(撤销commit,撤销add,代码彻底回滚(出错分支的新代码消失))彻底回退到某个版本,本地的源码也会变为上一个版本的内容
git reset --hard <commit_id>
#强行提交此版本到远程库,可以删除提交记录
git push origin HEAD --force

远程库

  • 要关联一个远程库,使用命令git remote add origin git@server-name:path/repo-name.git
  • 关联后,使用命令git push -u origin master第一次推送master分支的所有内容;
  • 此后,每次本地提交后,只要有必要,就可以使用命令git push origin master推送最新修改;
  • 克隆库 git clone

    分支

  • 查看分支:git branch
  • 创建分支:git branch <name>
  • 切换分支:git checkout <name>
  • 创建+切换分支:git checkout -b <name>
  • 合并某分支到当前分支:git merge <name>
  • 删除分支:git branch -d <name>
  • 出现分支冲突需要先解决冲突
  • git graph可以看到分支图,参数--abbrev-commit--pretty=oneline可以按照简短方式输出

    分支管理策略

  • 在实际开发中,我们应该按照几个基本原则进行分支管理:
  • 首先,master分支应该是非常稳定的,也就是仅用来发布新版本,平时不能在上面干活;
  • 干活都在dev分支上,也就是说,dev分支是不稳定的,到某个时候,比如1.0版本发布时,再把dev分支合并到master上,在master分支发布1.0版本;
  • 你和你的小伙伴们每个人都在dev分支上干活,每个人都有自己的分支,时不时地往dev分支上合并就可以了。
  • 所以,团队合作的分支看起来就像这样:
  • 如果出现之前BUG需要修复,则需要建立bug分支,首先将当前工作保存git stash,之后简历bug分支,切入bug分支修复bug后commit,切回工作分支merge,之后恢复工作状态git stash apply stash@{id号},或者使用git stash pop(恢复最新保存状态并删除其于stash list),git stash apply(恢复最新保存状态不删除其于stash list)git stash查看stash保存的状态列表,此时的工作状态文件中已经包含bug修复的内容
  • 开发一个新feature,最好新建一个分支;丢弃一个没有被合并过的分支,可以通过git branch -D <name>强行删除。

    多人协作

  • master分支是主分支,因此要时刻与远程同步;

  • dev分支是开发分支,团队所有成员都需要在上面工作,所以也需要与远程同步;

  • bug分支只用于在本地修复bug,就没必要推到远程了,除非老板要看看你每周到底修复了几个bug;

  • feature分支是否推到远程,取决于你是否和你的小伙伴合作在上面开发。

  • 另一个同伴参与开发,首先从远程git clone 下来远程的项目,此时只可以看到master分支,需要使用git checkout -b dev origin/dev签出正在开发的分支,如果出错则首先使用git fetch拉取最新的远程分支(之后可以进行本地方master分支合并),之后签出开发分支。

  • 伙伴此时可以随时修改、add、commit,进而push(git push origin branch-name)到远程分支,如果此时另一人正在push,则可能出错,首先git pull``获取并merge最新分支并解决冲突,之后再push即可。(此时pull如果失败则按照提示添加tracks,git branch branch-name --set-upstream-to=origin/branch-name

  • 查看远程库信息,使用git remote -v
  • 本地新建的分支如果不推送到远程,对其他人就是不可见的;

    标签

  • 命令git tag <name>用于新建一个标签,默认为HEAD,也可以指定一个commit id
  • git tag -a <tagname> -m "blablabla..."可以指定标签信息;
  • git tag -s <tagname> -m "blablabla..."可以用PGP签名标签;
  • 命令git tag可以查看所有标签。
  • 命令git push origin <tagname>可以推送一个本地标签;
  • 命令git push origin --tags可以推送全部未推送过的本地标签;
  • 命令git tag -d <tagname>可以删除一个本地标签;
  • 命令git push origin :refs/tags/<tagname>可以删除一个远程标签。

    自定义GIT

    显示颜色git config --global color.ui true
    忽略特殊文件 创建 .gitignore文件:
1
2
3
4
5
6
7
# Windows:
Thumbs.db
ehthumbs.db
Desktop.ini
# Python:
*.py[cod]
  • 命令别名:git config --global alias.st status(st==status)
  • 一般:co==checkout ci==commit b== branch

windows版本:

1、安装GIT,Node.js

2、安装Hexo:

1
npm install -g hexo

3、确定Hexo文件夹,在其处打开CMD进行初始化

1
hexo init

4、安装依赖

1
npm install

5、创建github版本库名称为xxx.github.io,xxx表示用户名

6、配置_config.yml文件的deploy节点,注意此处的url使用ssh格式,避免不可预知的问题

1
2
3
4
deploy:
type: git
repository: git@github.com:xxx/xxx.github.io.git
branch: master

7、配置SSH

1
2
3
4
5
6
7
8
9
10
11
12
13
14
ssh-keygen -t rsa -C "xxx@xx.com" //邮箱是注册github的时候用的,一路回车,别输入密钥之类的
ssh-agent -s
ssh-add ~/.ssh/id_rsa
//若出错
eval `ssh-agent -s`
ssh-add
//若出错 End
clip < ~/.ssh/id_rsa.pub //复制Key加入github的允许列表中
ssh -T git@github.com //测试出现Successful则成功

8、测试push

1
2
3
4
5
6
hexo generate //简写hexo g
hexo deploy //简写hexo d
//若ERROR Deployer not found: github
npm install hexo-deployer-git --save

9、主题更换,自行百度,eg. jacman

10、数学公式的支持

1
2
3
4
5
6
7
8
9
10
11
12
13
npm install hexo-math --save
//hexo 路径下
hexo math install
//_config.yml
Plugins:
- hexo-math
//写作中
{% math %}
$a+b$
{% endmath %}

11、无痛的在文章中插入图片

1
2
3
4
5
6
7
8
9
10
npm install https://github.com/CodeFalling/hexo-asset-image --save
//hexo new "test"时候,生成以下目录
test
├── aa.jpg
├── bb.jpg
└── cc.jpg
test.md
//写作中引入图片
![aa](aa.jpg "这是aa")

12、安装RSS插件

  • 装好后hexo g会在public目录产生atom.xml文件
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    # save很关键
    npm install hexo-generator-feed --save
    # 根目录_config.xml Plugins添加
    # Extensions
    Plugins:
    - hexo-generator-feed
    # Feed Atom
    feed:
    type: atom
    path: /atom.xml
    limit: 20
    # 主题配置文件添加(根据主题不同而不同)
    #### RSS
    rss: /atom.xml