目录
一、Hello World
#!/usr/local/bin/lua
print("helloWorld");
lua test.lua
二、注释
单行注释: -- 注释
多行注释:
--[[
注释内容
--]]
三、标识符
不允许使用特殊字符(@ $ % 等),只允许字母、数字、下划线,区分大小写
四、关键字
一般9约定以下划线开头连接一串大写字母的名字(_VERSION)被保留用于 Lua 内部全局变量
五、全局变量
#!/usr/local/bin/lua
print(b) -- 访问一个未定义的全局变量合法,值为 nil
c = 10
print(c)
c = nil -- 若要删除一个变量,将其赋值为 nil
六、Lua 数据类型
- nil :无效值
- boolean:包含 true / false
- number:双精度double类型的实浮点数
- string:一对 单引号或双引号表示的字符窜
- function:函数
- userdata:任意存储在变量中的C数据结构
- thread:执行的独立线程
- table:关联数组
type() :获取某个变量的数据类型
七、Lua 数据类型 - String
#!/usr/local/bin/lua
string1 = "helloworld" -- 字符窜
string2 = [[
i love china
i love asia
]] -- 表示一块字符窜
-- print(string1 + 2) -- 字符串与数字相加时,Lua会尝试这个数据字符串转成数字,若失败会报错
string3 = string1..string2 -- 通过 .. 连接字符串
print(#string3) -- #,放在字符窜前,用于计算字符串长度
八、Lua 数据类型 - table
#!/usr/local/bin/lua
-- table 可看做是关联数组,索引可以是数字或字符串
local table1 = {} -- 创建一个空table
local table2 = {"apple", "pear"} -- 创建与初始化一个table
table1["love"] = 1 -- 直接赋值
for k, v in pairs(table2) do
print(k, v)
end -- 循环table
-- Lua中初始索引为1
九、Lua 数据类型 - function
#!/usr/local/bin/lua
-- 重点1:函数可以存在变量中
function test(n)
if n == 1 then
return 1
else
return 0
end
end
print(test(1)) -- 调用函数
test2 = test -- 将函数test存放在test2中
print(test2(1)) -- 相当于调用test函数
-- 重点2:function 可以以匿名函数的方式通过参数传递
function test3(n, func)
func(n)
end
test3(1, function(x) -- 匿名函数的使用
return x
end)
十、Lua 数据类型 - userdata
表示一种由应用程序或 C 创建的类型,将 C 的任意数据类型数据(struct、指针等)存储到 Lua 中调用。
十一、Lua 变量
#!/usr/local/bin/lua
-- 重点1:全局变量 / 局部变量
a = 5
local b = 10 -- 显式指定为局部变量
function joke()
c = 10 -- 默认是全局变量
local d = 20 -- 设置为局部变量
end
joke()
print(c, d) -- 返回 10, nil
-- 重点2:同时赋值
x, y = 10, 20
x2 = 10; y2 = 10
-- 重点3:table中当索引为字符串类型时,可以通过 . 进行访问
table = {}
table["love"] = 1
print(table.love)
十二、Lua 循环
#!/usr/local/bin/lua
-- for 循环
-- var 从 exp1 变化到 exp2,每次变化以 exp3 为步长递增 var,并执行一次 "执行体"。exp3 是可选的,如果不指定,默认为1
-- for var = exp1, exp2, exp3 do
-- <执行体>
-- end
for i = 1, 100, 10 do
print(i)
end
-- while 循环
x = 0
while (x < 10)
do
x = x + 5
print(x)
end
-- repeat..until 循环
a = 0
repeat
print(a)
a = a + 5
until(a > 9)
需要注意的是 Lua 中 0 为 true
十三、Lua 函数
1)通过 local 关键字指定为局部函数,否则默认是全局函数
2)函数可返回多个值 return a, b
3)可变参数:
#!/usr/local/bin/lua
-- 计算平均值的函数
function average(...) -- 通过 ... 声明函数的参数可变,可传入不确定的参数个数。
result = 0
local arg = {...} -- lua将参数放在arg表中
for i, v in ipairs(arg) do
result = result + v
end
return result / #arg
end
print(average(1, 2, 3, 4, 5))
十四、运算符
1)不等于 ~=,^ 幂运算,如 a = 10 , a ^ 2 = 100
2)支持 and or not 判断,如 if (a and b)
3)连接两个字符串 .. ,如 a .. b
4)获取字符串或table的长度,如 # "Hello" = 5
十五、迭代器
#!/usr/local/bin/lua
-- 重点1:无状态迭代器,避免创建闭包花费的额外代价
function square(max, current)
if current < max then
current = current + 1
return current, current * current
end
end
for i, n in square, 3, 0 -- 调用迭代器
do
print(i, n)
end
-- 重点2:多状态迭代器
array = {"Lua", "Php"}
function iter(collection)
local index = 0
local count = #collection
return function () -- 闭包函数
index = index + 1
if index <= count then
return collection[index] -- 返回迭代器当前元素
end
end
end
for el in iter(array)
do
print(el)
end
十六、Lua 模块与包
1)基本使用
定义一个模块 module.lua:
#!/usr/local/bin/lua
module = {} -- Lua 的模块是由变量、函数等元素组成的表
module.constant = "这是一个常量" -- Lua 定义包中一个常量
function module.func1() -- 模块外可访问的函数
print("这是一个公有函数")
end
local function func2() -- 私有函数,只能本程序内部使用
print("这是一个私有函数")
end
function module.func3() -- 让私有函数在外部可以访问
func2()
end
return module -- 返回这个模块
定义一个调用模块的 lua 文件 useModule.lua :
#!/usr/local/bin/lua
local m = require("module") -- 引入模块
print(m.constant) -- 获取模块的值
m.func1()
执行 lua useModule.lua 可见 module 模块的值被引入
2)加载机制
require 用于搜索lua文件的路径存放在 package.path 这个全局变量中。Lua 启动后,会以环境变量 LUA_PATH 的值初始化这个环境变量。如果没有找不到该环境变量,使用编译时的默认路径初始化。
如果找到目标文件,调用 package.loadfile 加载模块,否则就找 C 程序库,从 package.cpath 获取搜索文件路径,搜索类似 .so 或 .dll 文件,找到通过 package.loadlib 加载
3)C包
Lua 与 C 可以很好地结合,可使用 C 为 Lua 写包。
local path = "/usr/local/lua/lib/libluasocket.so"
-- loadlib 函数提供动态链接功能,第一个参数是库的绝对路径,第二个参数是库中的初始化函数
local f = assert(loadlib(path, "luaopen_socket"))
f() -- 打开 C 包的库
十七、Lua 元表的使用
1)__index 元方法
#!/usr/local/bin/lua
-- 重点1:创建元表
mytable = {}
mymetatable = {}
setmetatable(mytable, mymetatable) -- 把mymetatable设置为mytale的元表
getmetatable(mytable) -- 返回mymetatable
-- 或者直接写成一行创建元表 mytable = setmetatable({}, {})
-- 重点2:__index 元方法
other = { foo = 3 }
table = setmetatable({}, { __index = other })
print(table.foo) -- 通过键访问表时,如果键没有值,寻找元表对应的 __index 方法获取
table2 = setmetatable({ foo = 4 }, { __index = other })
print(table.foo) -- 返回3而不返回4,说明 __index中若有访问的键对应的值,优先级要高于表中的键值
table3 = setmetatable({}, { __index = function(table3, key)
if key == 'key2' then
return "ok"
else
return nil
end
end
})
print(table3.key2) -- 演示 __ index 中使用函数返回
2)__newindex 元方法
#!/usr/local/bin/lua
-- __newindex 元方法
emptyTable = {}
table = setmetatable({key1 = "a"}, {__newindex = emptyTable})
print(table.key1) -- 如果键值存在直接返回
table.key2 = "b" -- 键值不存在则调用元表__newindex方法,将key2的属性赋值给 emptyTable
print(table.key2) -- nil
print(emptyTable.key2) -- "b"
3)为表添加运算符
#!/usr/local/bin/lua
-- 该函数求出table中最大的键
function maxTableKey(table)
local max = 0
for k, v in pairs(table)
do
if max < k then
max = k
end
end
return max
end
-- 两表相加
mytable = setmetatable({1, 2, 3}, {__add = function (mytable, newtable)
for i = 1, maxTableKey(newtable) do
table.insert(mytable, maxTableKey(mytable) + 1, newtable[i]);
-- 插入newtable的元素到mytable表中
end
return mytable -- 返回插入后的表
end
})
testtable = {4, 5, 6}
mytable = mytable + testtable -- 自动调用元表的 __add 方法
for k, v in ipairs(mytable) do
print(v)
end
4)__call 元方法
#!/usr/local/bin/lua
table = setmetatable({1, 2, 3}, {__call = function (table, n)
print("call me "..n)
end
})
print(table(100)) -- 当元表当做函数被使用时,调用__call方法
5)__tostring 元方法
#!/usr/local/bin/lua
table = setmetatable({}, { __tostring = function ()
return "i am tostringing"
end
})
print(table) -- 打印一个table变量时会调用__tostring元方法
十八、Lua 协程
Lua 协程与线程类似,拥有独立的堆栈、独立的局部变量、独立的指令指针
一个具有多线程的程序可以运行几个线程,而协程需要彼此协作运行。在任一指定时刻,只有一个协同程序在运行,且这个正运行的协程只有在明确被挂起的时候才被挂起。
coroutine.create() | 创建 coroutine , 返回 coroutine,参数是一个函数 |
coroutine.resume() | 重启 coroutine,和 create 配合使用 |
coroutine.yield() | 挂起 coroutine |
coroutine.status() | 查看 coroutine 状态:dead \ suspend \ running |
coroutine.warp() | 作用与 create 相同,调用返回的函数就进入 coroutine |
coroutine.running() | 返回正在运行的协程与布尔值,true代表当前是主协程 |
#!/usr/local/bin/lua
co = coroutine.create(function(i)
print(i)
end
)
coroutine.resume(co, 1) -- 输出1
print(coroutine.status(co)) -- 输出dead
#!/usr/local/bin/lua
co = coroutine.wrap(function(i)
print(i)
end
)
co(1) -- 输出1
#!/usr/local/bin/lua
co = coroutine.create(function() -- create 创建一个 coroutine
for i = 1, 10
do
print(i)
if i == 3 then
print(coroutine.status(co)) -- 查看coroutine状态
print(coroutine.running()) -- 查看正在运行的coroutine
end
coroutine.yield() -- 挂起coroutine
end
end
)
coroutine.resume(co) -- 重启coroutine , 输出1
coroutine.resume(co) -- 重启被挂起的coroutine,输出2
coroutine.resume(co) -- 功能同上,输出3
print(coroutine.status(co))
print(coroutine.running()) -- true 代表是主协程
-- 最终输出:
1
2
3
running
thread: 0x2106bb8 false
suspended
thread: 0x2100018 true
#!/usr/local/bin/lua
function foo(a)
print("foo_out", a)
return coroutine.yield(2*a) -- yield的参数将返回给resume
end
co = coroutine.create(function(a, b)
print("first ", a, b)
local r = foo(a + 1)
print("second ", r)
local r, s = coroutine.yield(a + b, a - b)
print("third ", r, s)
return b, "over"
end
)
print(coroutine.resume(co, 1, 10))
-- 第一次调用,将1,10传入给create的a b 参数,直到第5行函数挂起,返回2*a
print(coroutine.resume(co, "r"))
-- 第二次调用,由于协程已经产生,重启协程并将参数 r 赋值给第5行被挂起的断点 coroutine.yield ,因而 r = "r" 即此时 coroutine.yield 将 return "r" 使得第11行 local r = "r"
print(coroutine.resume(co, "x", "y"))
print(coroutine.resume(co, "x", "y"))
-- 输出:
--[[
--first 1 10
--foo_out 2
--true 4
--second r
--true 11 -9
--third x y
--true 10 over
--false cannot resume dead coroutine
--]]
十九、 Lua 垃圾回收
Lua 采用自动内存管理机制,通过运行垃圾收集器收集所有死对象(Lua中不可能再访问到的对象)来完成自动内存管理工作。
Lua 通过控制垃圾收集器间歇率(需要开启新的循环前等待的时间)、步进倍率(控制收集器运作速度相对于内存分配的速度)来对垃圾进行回收
#!/usr/local/bin/lua
table = {"apple", "orange", "juice"}
print(collectgarbage("count")) -- 查看此时 lua 使用的总内存数,以kb为单位
table = nil
print(collectgarbage("count")) -- 此时还没被回收
print(collectgarbage("collect")) -- 做一次完整的垃圾收集循环
print(collectgarbage("count")) -- 查看回收后使用的内存
--------------------
23.640625
23.744140625
0
23.0546875
二十、Lua 面向对象
Lua 使用表描述对象的属性,Lua 的函数用来表示方法,类可通过表+函数模拟出来。
1)简单创建类
#!/usr/local/bin/lua
Shape = {area = 0} -- Shape 作为类的元表
function Shape:new (o, side) -- 声明一个类,相当于 __ construct() 类的构造方法
o = o or {}
setmetatable(o, self) -- 这里的self指的是Shape
self.__index = self -- 当访问o.area,因为o中没有area,因此会访问Shape中的area
side = side or 0
self.area = side * side -- 声明新的类属性
self.hello = 1
return o
end
function Shape:printArea() -- 声明类的成员方法
print("Area is ", self.area)
end
-- 创建对象
shaper = Shape:new(nil, 10)
shaper:printArea() -- 访问方法
print(shaper.hello) -- 访问成员属性
2)继承
#!/usr/local/bin/lua
Shape = { area = 1 }
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
function Shape:printArea()
print(self.area)
end
-- 继承
Square = Shape:new()
function Square:new(o, side)
o = o or Shape:new(o, side) -- 使用 Shape 表作为父类
setmetatable(o, self)
self.__index = self
return o
end
-- 使用继承类
square = Square:new(nil, 10)
square:printArea()