0.引用
1. 变量和流程控制
1.1 变量
-- 单行注释
--[[
多行注释
--]]
num = 42 -- 对于52位以内的整型值, 不用担心精度问题。
s = 'walternate' -- 字符串不可变。
t = "也可以用双引号"
u = [[ 多行的字符串
以两个方括号
开始和结尾。]]
t = nil -- 撤销定义; Lua 支持垃圾回收。
-- Lua 会尝试将这个数字字符串转成一个数字
-- 未定义的变量返回nil,这不是错误:
foo = anUnknownVariable -- 现在 foo = nil.
aBoolValue = false
--只有nil和false为假; 0和 ''均为真!
if not aBoolValue then print('false') end
-- 'or'和 'and'短路
-- 类似于C/js里的 a?b:c 操作符:
ans = aBoolValue and 'yes' or 'no' --> 'no'
1.2运算符优先级
--高 --》 低
^ --右连接
not - (unary)
* /
+ -
.. --右连接
< > <= >= ~= ==
and
or
--除了 ^ 和 .. 外所有的二元运算符都是左连接的。
1.3 流程控制
1.3.1 while
-- 块使用do/end之类的关键字标识:
while num < 50 do
num = num + 1 -- 不支持 ++ 或 += 运算符。
end
1.3.2 If语句
-- If语句:
if num > 40 then
print('over 40')
elseif s ~= 'walternate' then -- ~= 表示不等于,用 == 是否相等 ;字符串同样适用。
print('not over 40\n')
else
-- 默认全局变量。
thisIsGlobal = 5
-- 如何定义局部变量:
local line = io.read() -- 读取标准输入的下一行。
-- ..操作符用于连接字符串:
io.write('Winter is coming, ' .. line)
end
1.3.3 for & repeat
karlSum = 0
for i = 1, 100 do -- 范围包含两端 [1,100]
karlSum = karlSum + i
end
-- 使用 "100, 1, -1" 表示递减的范围:
fredSum = 0
for j = 100, 1, -1 do
fredSum = fredSum + j
end
-- 通常,范围表达式为begin, end[, step].
-- 循环的另一种结构:
repeat
print('the way of the future')
num = num - 1
until num == 0
2. 函数
function fib(n)
if n < 2 then return n end
return fib(n - 2) + fib(n - 1)
end
-- 支持闭包及匿名函数:
function adder(x)
-- 调用adder时,会创建返回的函数,
-- 并且会记住x的值:
return function (y) return x + y end --匿名函数 function (y)
end
a1 = adder(9)
a2 = adder(36)
print(a1(16)) --> 25
print(a2(64)) --> 100
-- 返回值、函数调用和赋值都可以使用长度不匹配的list。
--a. 变量个数 > 值的个数 按变量个数补足nil
--b. 变量个数 < 值的个数 多余的值会被忽略
x, y, z = 1, 2, 3, 4
-- x = 1、y = 2、z = 3, 而 4 会被丢弃。
function bar(a, b, c)
print(a, b, c)
return 4, 8, 15, 16, 23, 42
end
x, y = bar('zaphod') --> 打印 "zaphod nil nil"
-- 现在 x = 4, y = 8, 而值15..42被丢弃。
-- 函数是一等公民,可以是局部的,也可以是全局的。
-- 以下表达式等价:
function f(x) return x * x end
f = function (x) return x * x end
-- 等价:
local function g(x) return math.sin(x) end
local g;
g = function (x) return math.sin(x) end
local g = function(x) return math.sin(x) end
-- 调用函数时,只有一个字符串、table参数,省略括号:
print 'hello' --可以工作。
print {} --可以工作。
3. 组合数据结构Table
3.1 Table基础
-- Table = Lua唯一的组合数据结构;
-- Dict/map字面量默认使用字符串类型的key:
t = {key1 = 'value1', key2 = false}
-- 字符串key点标记:
print(t.key1) -- 打印 'value1'.
t.newKey = {} -- 添加新的键值对。
t.key2 = nil -- 从table删除 key2。
t[i]
t.i -- 当索引为字符串类型时的一种简化写法
gettable_event(t,i) -- 采用索引访问本质上是一个类似这样的函数调用
-- 使用任何非nil的值作为key:
u = {['@!#'] = 'qbert', [{}] = 1729, [6.28] = 'tau'}
print(u[6.28]) -- 打印 "tau"
-- 数字和字符串的key按值匹配的,所以字符串和数字是移植性更好的key。
-- table按id匹配。
a = u['@!#'] -- 现在 a = 'qbert'.
b = u[{}] -- 我们得到 b = nil ,因为没有找到。
-- 只需要一个table参数的函数调用不需要括号:
function h(x) print(x.key1) end
h{key1 = 'Sonmi~451'} -- 打印'Sonmi~451'.
for key, val in pairs(u) do -- 遍历Table
print(key, val)
end
-- _G 是一个特殊的table,用于保存所有的全局变量
print(_G['_G'] == _G) -- 打印'true'.
-- 按列表/数组的方式使用:
-- 列表字面量隐式添加整数 key键:
v = {'value1', 'value2', 1.21, 'gigawatts'}
for i = 1, #v do -- #v 是列表的大小
print(v[i]) -- 索引从 1 开始!! 太疯狂了!
end
-- 'list'并非真正的类型,v 其实是一个table,
-- 只不过它用连续的整数作为key,可以像list那样去使用。
function average(...)
result = 0
local arg={...} --> {...} 表示一个由所有变长参数构成的数组
for i,v in ipairs(arg) do
result = result + v
end
print("总共传入 " .. select("#",...) .. " 个数")
return result/select("#",...)
end
print("平均值为",average(10,5,3,4,5,6))
--固定参数必须放在变长参数之前:
3.1.1Lua 查找表元素步骤:
--1.在表中查找,如果找到,返回该元素,找不到则继续.
--2.判断该表是否有元表,如果没有元表,返回 nil,有元表则继续。
--3.判断元表有没有 __index 方法,
如果 __index 方法为 nil,则返回 nil;
如果 __index 方法是一个表,则重复 1、2、3;
如果 __index 方法是一个函数,则返回该函数的返回值。
3.1.2 __newindex 元方法
__newindex 元方法用来对表更新,__index则用来对表访问, key存在不更新。
mymetatable = {}
mytable = setmetatable({key1 = "value1"}, { __newindex = mymetatable })
print(mytable.key1) --value1
mytable.newkey = "新值2"
print(mytable.newkey,mymetatable.newkey) --nil 新值2
mytable.key1 = "新值1"
print(mytable.key1,mymetatable.key1) --新值1 nil
3.1.3 table类附加操作
1 |
table.concat (table [, sep [, start [, end]]]): start位置到end位置的所有元素,以指定的(sep)隔开。 |
2 | table.insert (table, [pos,] value):在table的数组部分(pos)插value的元素. pos参数可选, 默认末尾. |
3 | table.sort (table [, comp])对给定的table进行升序排序。 |
4 | table.remove (table [, pos])返回table位于pos位置的元素. 其后前移. pos可选, 默认从最后元素删起。 |
3.2 Table高级
3.2.1 操作符重载(metatable和metamethod )
-- table的元表提供了一种机制,支持类似操作符重载的行为。
f1 = {a = 1, b = 2} -- 表示一个分数 a/b.
f2 = {a = 2, b = 3}
-- 这会失败:
-- s = f1 + f2
metafraction = {}
function metafraction.__add(f1, f2)
local sum = {}
sum.b = f1.b * f2.b
sum.a = f1.a * f2.b + f2.a * f1.b
return sum
end
setmetatable(f1, metafraction)
setmetatable(f2, metafraction)
s = f1 + f2 -- 调用在f1的元表上的__add(f1, f2) 方法
-- f1, f2 没有关于元表的key,这点和js的prototype不一样。
-- 因此你必须用getmetatable(f1)获取元表。
-- 元表是一个普通的table,
-- 元表的key是普通的Lua中的key,例如__add。
-- 但是下面一行代码会失败,因为s没有元表:
-- t = s + s
-- 下面提供的与类相似的模式可以解决这个问题:
-- 元表的__index 可以重载用于查找的点操作符:
defaultFavs = {animal = 'gru', food = 'donuts'}
myFavs = {food = 'pizza'}
setmetatable(myFavs, {__index = defaultFavs})
eatenBy = myFavs.animal -- 可以工作!感谢元表
-- 如果在table中直接查找key失败,会使用
-- 元表的__index 递归地重试。
-- __index的值也可以是function(tbl, key)
-- 这样可以支持自定义查找。
-- __index、__add等的值,被称为元方法。
-- 这里是一个table元方法的清单:
-- __add(a, b) for a + b
-- __sub(a, b) for a - b
-- __mul(a, b) for a * b
-- __div(a, b) for a / b
-- __mod(a, b) for a % b
-- __pow(a, b) for a ^ b
-- __unm(a) for -a
-- __concat(a, b) for a .. b
-- __len(a) for #a
-- __eq(a, b) for a == b
-- __lt(a, b) for a < b
-- __le(a, b) for a <= b
-- __index(a, b) <fn or a table> for a.b
-- __newindex(a, b, c) for a.b = c
-- __call(a, ...) for a(...)
3.2.2 类结构
-- Lua没有内建的类;可以通过不同的方法,利用表和元表
-- 来实现类。
Dog = {} -- 1. 表
function Dog:new() -- 2.
local newObj = {sound = 'woof'} -- 3.
self.__index = self -- 4.
return setmetatable(newObj, self) -- 5. 元表
end
function Dog:makeSound() -- 6.
print('I say ' .. self.sound)
end
mrDog = Dog:new() -- 7.
mrDog:makeSound() -- 'I say woof' -- 8.
-- 1. Dog看上去像一个类;其实它是一个table。
-- 2. 函数tablename:fn(...) 等价于
-- 函数tablename.fn(self, ...)
-- 冒号(:)只是添加了self作为第一个参数。
-- 阅读7 & 8条 了解self变量是如何得到其值的。
-- 3. newObj是类Dog的一个实例。
-- 4. self = 被继承的类。通常self = Dog,不过继承可以改变它。
-- 如果把newObj的元表和__index都设置为self,
-- newObj就可以得到self的函数。
-- 5. 备忘:setmetatable返回其第一个参数。
-- 6. 冒号(:)的作用和第2条一样,不过这里
-- self是一个实例,而不是类
-- 7. 等价于Dog.new(Dog),所以在new()中,self = Dog。
-- 8. 等价于mrDog.makeSound(mrDog); self = mrDog。
3.2.3 类继承实现
-- 继承的例子:
LoudDog = Dog:new() -- 1.
function LoudDog:makeSound()
local s = self.sound .. ' ' -- 2.
print(s .. s .. s)
end
seymour = LoudDog:new() -- 3.
seymour:makeSound() -- 'woof woof woof' -- 4.
-- 1. LoudDog获得Dog的方法和变量列表。
-- 2. 因为new()的缘故,self拥有了一个'sound' key,参见第3条。
-- 3. 等价于LoudDog.new(LoudDog),转换一下就是
-- Dog.new(LoudDog),这是因为LoudDog没有'new' key,
-- 但是它的元表中有 __index = Dog。
-- 结果: seymour的元表是LoudDog,并且
-- LoudDog.__index = Dog。所以有seymour.key
-- = seymour.key, LoudDog.key, Dog.key
-- 从其中第一个有指定key的table获取。
-- 4. 在LoudDog可以找到'makeSound'的key;
-- 等价于LoudDog.makeSound(seymour)。
-- 如果有必要,子类也可以有new(),与基类相似:
function LoudDog:new()
local newObj = {}
-- 初始化newObj
self.__index = self
return setmetatable(newObj, self)
end
4. 模块
-- 假设文件mod.lua的内容类似这样:
local M = {}
local function sayMyName()
print('Hrunkner')
end
function M.sayHello()
print('Why hello there')
sayMyName()
end
return M
-- 另一个文件可以使用mod.lua的功能:
local mod = require('mod') -- 运行文件mod.lua.
-- require是包含模块的标准做法。
-- require等价于: (针对没有被缓存的情况;参见后面的内容)
local mod = (function ()
<contents of mod.lua>
end)()
-- mod.lua被包在一个函数体中,因此mod.lua的局部变量
-- 对外不可见。
-- 下面的代码可以工作,因为在这里mod = mod.lua 中的 M:
mod.sayHello() -- Says hello to Hrunkner.
-- 这是错误的;sayMyName只在mod.lua中存在:
mod.sayMyName() -- 错误
-- require返回的值会被缓存,所以一个文件只会被运行一次,
-- 即使它被require了多次。
-- 假设mod2.lua包含代码"print('Hi!')"。
local a = require('mod2') -- 打印Hi!
local b = require('mod2') -- 不再打印; a=b.
-- dofile与require类似,但是不缓存:
dofile('mod2') --> Hi!
dofile('mod2') --> Hi! (再次运行,与require不同)
-- loadfile加载一个lua文件,但是并不运行它。
f = loadfile('mod2') -- Calling f() runs mod2.lua.
-- loadstring是loadfile的字符串版本。
g = loadstring('print(343)') --返回一个函数。
g() -- 打印343; 在此之前什么也不打印。
--c 动态库
local path = "/usr/local/lua/lib/libluasocket.so"
local f = loadlib(path, "luaopen_socket")
5. thread(线程)
5.1协同程序(coroutine)
在 Lua 里,最主要的线程是协同程序(coroutine)。它跟线程(thread)差不多,拥有自己独立的栈、局部变量和指令指针,可以跟其他协同程序共享全局变量和其他大部分东西。
线程跟协程的区别:线程可以同时多个运行,而协程任意时刻只能运行一个,并且处于运行状态的协程只有被挂起(suspend)时才会暂停。
方法 | 描述 |
---|---|
coroutine.create() | 创建 coroutine,返回 coroutine, 参数是一个函数,当和 resume 配合使用的时候就唤醒函数调用 |
coroutine.resume() | 重启 coroutine,和 create 配合使用 |
coroutine.yield() | 挂起 coroutine,将 coroutine 设置为挂起状态,这个和 resume 配合使用能有很多有用的效果 |
coroutine.status() | 查看 coroutine 的状态 注:coroutine 的状态有三种:dead,suspended,running,具体什么时候有这样的状态请参考下面的程序 |
coroutine.wrap() | 创建 coroutine,返回一个函数,一旦你调用这个函数,就进入 coroutine,和 create 功能重复 |
coroutine.running() | 返回正在跑的 coroutine,一个 coroutine 就是一个线程,当使用running的时候,就是返回一个 corouting 的线程号 |
例子
-- coroutine_test.lua 文件
co = coroutine.create(function(i) print(i); end)
coroutine.resume(co, 1) -- 1
print(coroutine.status(co)) -- dead
print("----------")
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()
end
end
)
coroutine.resume(co2) --1
coroutine.resume(co2) --2
coroutine.resume(co2) --3
print(coroutine.status(co2)) -- suspended
print(coroutine.running())
print("----------")
运行结果
1
dead
----------
1
----------
1
2
3
running
thread: 0x7fb801c05868 false
suspended
thread: 0x7fb801c04c88 true
----------
6. userdata(自定义类型)
userdata 是一种用户自定义数据,用于表示一种由应用程序或 C/C++ 语言库所创建的类型,可以将任意 C/C++ 的任意数据类型的数据(通常是 struct 和 指针)存储到 Lua 变量中调用。
7. string
7.1 基本函数
string.upper(argument)
string.lower(argument)
string.gsub(mainString,findString,replaceString,num) --default all
string.find (str, substr, [init, [end]]) --init为索引返回其pos or nil。
string.reverse(arg) --字符串反转
string.format("the value is:%d",4)
string.char(97,98,99,100) --abcd
string.byte("ABCD",4) --68
string.len("abc") --3
string.rep("a",3) --aaa
print("aaa.".."bbb") --aaabbb
string.match("I have 2 questions for you.", "%d+ %a+")
--2 questions
string.format("%d, %q", string.match("I have 2 questions for you.", "(%d+) (%a+)"))
--2, "questions"
string.format("%c", 83) -- 输出S
string.format("%+d", 17.0) -- 输出+17
string.format("%05d", 17) -- 输出00017
string.format("%o", 17) -- 输出21
string.format("%u", 3.14) -- 输出3
string.format("%x", 13) -- 输出d
string.format("%X", 13) -- 输出D
string.format("%e", 1000) -- 输出1.000000e+03
string.format("%E", 1000) -- 输出1.000000E+03
string.format("%6.3f", 13) -- 输出13.000
string.format("%q", "One\nTwo") -- 输出"One\
-- Two"
string.format("%s", "monkey") -- 输出monkey
string.format("%10s", "monkey") -- 输出 monkey
string.format("%5.3s", "monkey") -- 输出 mon 只显示前n位.
--%q - 接受一个字符串并将其转化为可安全被Lua编译器读入的格式
--%s - 接受一个字符串并按照给定的参数格式化该字符串
--(1) +:正数显示正号.
--(2) 占位符: 0. 默认占位符是空格.
--(3) 对齐标识: 在指定了字串宽度时, 默认为右对齐, 增加-号可以改为左对齐.
--(4) 宽度数值
7.2 模式匹配
它用于模式匹配函数 string.find, string.gmatch, string.gsub, string.match。
. : 与任何字符配对
%a: 与任何字母配对
%c: 与任何控制符配对(例如\n)
%d: 与任何数字配对
%l: 与任何小写字母配对
%p: 与任何标点(punctuation)配对
%s: 与空白字符配对
%u: 与任何大写字母配对
%w: 与任何字母/数字配对
%x: 与任何十六进制数配对
%z: 与任何代表0的字符配对
%x:(此处x是非字母非数字字符): 与字符x配对. 主要用来处理表达式中有功能的字符(^$()%.[]*+-?)的配对问题, 例如%%与%配对
[数个字符类]: 与任何[]中包含的字符类配对. 例如[%w_]与任何字母/数字, 或下划线符号(_)配对
[^数个字符类]: 与任何不包含在[]中的字符类配对. 例如[^%s]与任何非空白字符配对
8. 文件 I/O
I/O 库用于读取和处理文件。分为简单模式(和C一样)、复杂模式(C++模式)。
file = io.open (filename [, mode])
8.1 mode枚举值
mode | 描述 |
---|---|
r | 以只读方式打开文件,该文件必须存在。 |
w | 打开只写文件,内容会消失。若文件不存在则建立该文件。 |
a | 以附加的方式打开只写文件。若文件不存在,则会建立该文件,如果文件存在,写入的数据会被加到文件尾,即文件原先的内容会被保留。(EOF符保留) |
r+ | 以可读写方式打开文件,该文件必须存在。 |
w+ | 打开可读写文件,该文件内容会消失。若文件不存在则建立该文件。 |
a+ | 可读可写 |
b | 二进制 |
+ | 可读写 |
8.2 用例1
-- 以只读方式打开文件
file = io.open("test.lua", "r")
-- 设置默认输入文件为 test.lua
io.input(file)
-- 输出文件第一行
print(io.read())
-- 关闭打开的文件
io.close(file)
-- 以附加的方式打开只写文件
file = io.open("test.lua", "a")
-- 设置默认输出文件为 test.lua
io.output(file)
-- 在文件最后一行添加 Lua 注释
io.write("-- test.lua 文件末尾注释")
-- 关闭打开的文件
io.close(file)
io.read()
"*n" --读取一个数字并返回它。例:io.read("*n")
"*a" --从当前位置读取整个文件。例:io.read("*a")
"*l"--(默认) 读取下一行,在文件尾 (EOF) 处返回 nil。例:io.read("*l")
number --返回一个指定字符个数的字符串,或在 EOF 时返回 nil。例:io.read(5)
8.3 用例2
-- 以只读方式打开文件
file = io.open("test.lua", "r")
-- 输出文件第一行
print(file:read())
-- 关闭打开的文件
file:close()
-- 以附加的方式打开只写文件
file = io.open("test.lua", "a")
-- 在文件最后一行添加 Lua 注释
file:write("--test")
-- 关闭打开的文件
file:close()
9.Lua 错误处理
9.1 assert
local function add(a,b)
assert(type(a) == "number", "a 不是一个数字")
assert(type(b) == "number", "b 不是一个数字")
return a+b
end
add(10) --"b 不是一个数字"
9.2 error
error (message [, level])
-- Level=1[默认]:为调用error位置(文件+行号)
-- Level=2:指出哪个调用error的函数的函数
-- Level=0:不添加错误位置信息
9.3 pcall
pcall接收一个函数和要传递给后者的参数,捕获函数执行中的任何错误。成功返回true;失败false,errorinfo.但pcall返回时,它已经销毁了调用桟的部分内容。
if pcall(function_name, ….) then
-- 没有错误
else
-- 一些错误
end
9.4 xpcall
xpcall接收第二个参数——一个错误处理函数,当错误发生时,Lua会在调用桟展开(unwind)前调用错误处理函数,于是就可以在这个函数中使用debug库来获取关于错误的额外信息了。
function myfunction ()
n = n/nil
end
function myerrorhandler( err )
print( "ERROR:", err )
end
status = xpcall( myfunction, myerrorhandler )
print( status)
--ERROR: test2.lua:2: attempt to perform arithmetic on global 'n' (a nil value)
--false
xpcall(function(i) print(i) error('error..') end, function() print(debug.traceback()) end, 33)
10. debug
序号 | 方法 & 用途 |
---|---|
1. | debug():进入一个用户交互模式,运行用户输入的每个字符串。 使用简单的命令以及其它调试设置,用户可以检阅全局变量和局部变量, 改变变量的值,计算一些表达式,等等。 输入一行仅包含 cont 的字符串将结束这个函数, 这样调用者就可以继续向下运行。 |
2. | getfenv(object):返回对象的环境变量。 |
3. | gethook(optional thread):返回三个表示线程钩子设置的值: 当前钩子函数,当前钩子掩码,当前钩子计数 |
4. | getinfo ([thread,] f [, what]):返回关于一个函数信息的表。 你可以直接提供该函数, 也可以用一个数字 f 表示该函数。 数字 f 表示运行在指定线程的调用栈对应层次上的函数: 0 层表示当前函数(getinfo 自身); 1 层表示调用 getinfo 的函数 (除非是尾调用,这种情况不计入栈);等等。 如果 f 是一个比活动函数数量还大的数字, getinfo 返回 nil。 |
5. | debug.getlocal ([thread,] f, local):此函数返回在栈的 f 层处函数的索引为 local 的局部变量 的名字和值。 这个函数不仅用于访问显式定义的局部变量,也包括形参、临时变量等。 |
6. | getmetatable(value):把给定索引指向的值的元表压入堆栈。如果索引无效,或是这个值没有元表,函数将返回 0 并且不会向栈上压任何东西。 |
7. | getregistry():返回注册表表,这是一个预定义出来的表, 可以用来保存任何 C 代码想保存的 Lua 值。 |
8. | getupvalue (f, up)此函数返回函数 f 的第 up 个上值的名字和值。 如果该函数没有那个上值,返回 nil 。 以 '(' (开括号)打头的变量名表示没有名字的变量 (去除了调试信息的代码块)。 |
10. | sethook ([thread,] hook, mask [, count]):将一个函数作为钩子函数设入。 字符串 mask 以及数字 count 决定了钩子将在何时调用。 掩码是由下列字符组合成的字符串,每个字符有其含义:'c': 每当 Lua 调用一个函数时,调用钩子;'r': 每当 Lua 从一个函数内返回时,调用钩子;'l': 每当 Lua 进入新的一行时,调用钩子。 |
11. | setlocal ([thread,] level, local, value):这个函数将 value 赋给 栈上第 level 层函数的第 local 个局部变量。 如果没有那个变量,函数返回 nil 。 如果 level 越界,抛出一个错误。 |
12. | setmetatable (value, table):将 value 的元表设为 table (可以是 nil)。 返回 value。 |
13. | setupvalue (f, up, value):这个函数将 value 设为函数 f 的第 up 个上值。 如果函数没有那个上值,返回 nil 否则,返回该上值的名字。 |
14. | traceback ([thread,] [message [, level]]):如果 message 有,且不是字符串或 nil, 函数不做任何处理直接返回 message。 否则,它返回调用栈的栈回溯信息。 字符串可选项 message 被添加在栈回溯信息的开头。 数字可选项 level 指明从栈的哪一层开始回溯 (默认为 1 ,即调用 traceback 的那里)。 |
请发表评论