看云风的Skynet的时候, 对Lua生出了很大的兴趣,抽出时间看了下Lua在线手册,特作此记录。
本文仅为Lua语言作为独立的脚本语言的部分知识速记。 虽然Lua被设计来作为宿主语言的嵌入脚本,不过简单的逻辑纯Lua也可以实现。
关键字和操作符
关键字
and break do else elseif
end false for function if
in local nil not or
repeat return then true until while
这些关键字包含了 :
- 类型(值)关键字
nil true false function
- 控制流关键字
do while
,repeat until
,if then else elseif
,for in
, 和end break return
。 - 逻辑运算关键字
and or not
。 - 作用域关键字
local
。 - 以下常规关键字没有提供 :
continue
。main
。
操作符
+ - * / % ^ #
== ~= <= >= < > =
( ) { } [ ]
; : , . .. ...
- 不等于是
~=
而不是!=
。 - 没有逻辑运算符
&& || !
, 使用对应关键字 。 ..
表示字符串连接。#
运算符表示计算大小(长度)!
table = {'a' , 'b' ,[4]= '4', aa='bb'}
str = "12345"
print(#table)
print(#str)
输出
-- 从索引1开始顺序计数,直到数字索引中断。并不代表table的真正大小,仅代表```ipairs``` 接口的返回pair数。
2
-- 计数字符串中字符个数 \0 不计数。
5
类型
- nil
- 即是类型有事值 。
- 在判断中表示
false
- boolean
nil 、 false
表示false
。- 其余全部表示
true
包括0
和字符'\0'
。
- number
- 默认双精度
- 可以通过 luaconf.h 文件修改 (重新编译Lua解释器)。
- string
- 没有字符的概念,全部是字符串。
- 可以使用
' '
," "
或者[=*[ ]=*]
。 \0
结尾 。
- function
- function也是变量类型
- 支持闭包。
- userdata
- 表示 C (宿主) 数据 , 仅宿主代码可以创建或者修改 。
- 利用元表(
metatable
)为它指定操作函数。
- thread
- 协程类型,基于单线程,需要手动切入切出。
- table
- 默认索引是数字,从
1
开始 。 ( 数字 0 索引是可用的, 只是默认索引不会去不使用 ipairs 函数也不会去检测 数字 0) - 索引可以是除去
nil
之外的所有类型 。 - 一个table不必使用统一类型的索引。
- 值可以是除去
nil
之外的所有类型,a[i] = nil
意味着删除a[i]
。 - 语法糖
a[b]
等价于a.b
。a={["test"]=1}
等价于a= {test=1}
。
- 默认索引是数字,从
Table 的测试代码
-- Construct a table
table_a = {'test';["x"] = 111 ; 'Woo',y='xx', [0] = 'test 0 number'}
table_a[3]='2 set'
table_a[5]='5 set'
table_a['\0']='test 0 again'
table_a['0']='test 0 character'
-- Tarvel through a table's first part integer pairs
for i , v in ipairs(table_a)
do
print(i,v);
end
-- Tarvel through a table's all pairs
for i , v in pairs(table_a)
do
print(i,v);
end
输出 :
-- ipair 循环输出 :
1 test
2 Woo
3 2 set
-- pair 循环输出 :
1 test
2 Woo
3 2 set
5 5 set
y xx
x 111
0 test 0 again character -- 字符 0
test 0 again -- \0 字符不显示
0 test 0 number -- 数字 0
(CSDN-BUG)
语句
分号
lua的语句总是可以使用分号结尾, 但是在没有歧义的情况下也可以不使用。 条件判断的括号也是可有可无 。
判断
if condition then
elseif ( condition ) then
else
end
循环
while
while condition
do
...
end
repeat until
-- 其实这个就是C里面的 do ... while 循环
repeat
...
until condition
for
-- 给var一个初始值, 一个终止值 和一个步长, 循环之。
-- 步长可以省略 , 省略则为1。
for var = 1 , 10 , 2
do
...
end
for in
-- Lua的 foreach 循环
a_table = { 1, 12, 3}
for i , v in pairs(a_table)
do
...
end
(CSDN-BUG)
函数
函数定义
普通函数
-- 直接定义
function foo ( param1 )
-- like print(param1)
...
end
-- 赋值定义给某个变量
foo = function ( param1 )
...
end
添加到某个table / userdata 的metatable中
-- 定义给特定变量
-- 方法1 : 仅仅绑定方法
test={}
test.op = function(param1)
...
end
-- 然后如下使用
test.op('param')
-- 方法2 :绑定且默认传入 “:” 前的变量为第一个参数,参数名字是self 。
test={test1=1 , test2=2}
test:op = function(param1) -- 使用 : 而不是 .
-- 已经默认传入test 作为self参数 , 直接如下使用
print(self.test1 , self.test2)
...
end
-- 然后如下使用这个函数。
test:op('param') -- 使用 : 而不是 .
函数闭包
function newCounter()
local i = 0
return function() -- anonymous function who hold i variable
i = i + 1
return i
end
end
c1 = newCounter()
print(c1()) --> 1
print(c1()) --> 2
支持多返回值
参见
(CSDN-BUG)
metatable
定义
Lua 中的每个值都可以用一个 metatable。 这个 metatable 就是一个原始的 Lua table , 它用来定义原始值在特定操作下的行为。 你可以通过在 metatable 中的特定域设一些值来改变拥有这个 metatable 的值 的指定操作之行为。 举例来说,当一个非数字的值作加法操作的时候, Lua 会检查它的 metatable 中 “__add” 域中的是否有一个函数。 如果有这么一个函数的话,Lua 调用这个函数来执行一次加法。
我们叫 metatable 中的键名为 事件 (event) ,把其中的值叫作 元方法 (metamethod)。 在上个例子中,事件是 “add” 而元方法就是那个执行加法操作的函数。
你可以通过 getmetatable 函数来查询到任何一个值的 metatable。
你可以通过 setmetatable 函数来替换掉 table 的 metatable 。 你不能从 Lua 中改变其它任何类型的值的 metatable (使用 debug 库例外); 要这样做的话必须使用 C API 。
每个 table 和 userdata 拥有独立的 metatable (当然多个 table 和 userdata 可以共享一个相同的表作它们的 metatable); 其它所有类型的值,每种类型都分别共享唯一的一个 metatable。 因此,所有的数字一起只有一个 metatable ,所有的字符串也是,等等。
__add(a, b) --对应表达式 a + b
__sub(a, b) --对应表达式 a - b
__mul(a, b) --对应表达式 a * b
__div(a, b) --对应表达式 a / b
__mod(a, b) --对应表达式 a % b
__pow(a, b) --对应表达式 a ^ b
__unm(a) --对应表达式 -a
__concat(a, b) --对应表达式 a .. b
__len(a) --对应表达式 #a
__eq(a, b) --对应表达式 a == b
__lt(a, b) --对应表达式 a < b
__le(a, b) --对应表达式 a <= b
__index(a, b) --对应表达式 a.b
__newindex(a, b, c) --对应表达式 a.b = c
__call(a, ...) --对应表达式 a(...)
实例
重载 + 运算符
-- Create 2 table with same struct
fraction_a = {numerator=2, denominator=3}
fraction_b = {numerator=4, denominator=7}
-- Create a metatable
fraction_op={}
function fraction_op.__add(f1, f2)
ret = {}
ret.numerator = f1.numerator * f2.denominator + f2.numerator * f1.denominator
ret.denominator = f1.denominator * f2.denominator
return ret;
end
-- set metatable to table
setmetatable(fraction_a, fraction_op)
setmetatable(fraction_b, fraction_op)
-- call __add operator
fraction_s = fraction_a + fraction_b;
print(fraction_s.numerator,fraction_s.denominator)
利用metatable实现面向对象编程
-- 实现简单的继承
Father={ name = '1 Father'};
function Father:Print()
print('I am '..self.name);
end
Son={};
setmetatable(Son,{__index=Father});
-- Print I am 1 Father if Son.Print was not defined.
Son:Print();
--实现new接口
Person={}
function Person:new(p)
local obj = p
if (obj == nil) then
obj = {name="ChenHao", age=37, handsome=true}
end
self.__index = self
return setmetatable(obj, self)
end
function Person:toString()
return self.name .." : ".. self.age .." : ".. (self.handsome and "handsome" or "ugly")
end
me = Person:new()
print(me:toString())
kf = Person:new{name="King's fucking", age=70, handsome=false}
print(kf:toString())
(CSDN-BUG)
协程
接口
- 创建
- coroutine.create 返回thread
- coroutine.swap 返回function , 调用之久resume
- coroutine.resume 切入
- coroutine.yield 切出
- 查询
- coroutine.status
- coroutine.running
实例分析
function foo (a)
print("foo", a);
return coroutine.yield(2*a);
end
co = coroutine.create(function (a,b)
print("co-body", a, b);
local r = foo(a+1);
print("co-body-mid", r);
local r, s = coroutine.yield(a+b, a-b)
print("co-body-end", r, s);
return b, "end";
end);
print("main 1", coroutine.resume(co, 1, 10))
print("main 2", coroutine.resume(co, "e"))
print("main 3", coroutine.resume(co, "x", "y"))
print("main 4", coroutine.resume(co, "x", "y"))
输出 :
-- main coroutine start
-- enter co coroutine
co-body 1 10
-- call foo
foo 2
-- yield to main coroutine
main 1 true 4
-- resume to co coroutine
co-body-mid e
-- yield to main coroutine
main 2 true 11 -9
-- resume to co coroutine
co-body-end x y
-- yield to main coroutine
main 3 true 10 end
-- resume to co coroutine failed
main 4 false cannot resume dead coroutine
(CSDN-BUG)
模块
加载模块
require (不重复的)加载模块文件
下理解是错误的( 虽然这种解释有利于理解文件 + table的常规模块文件) :
fp=require(“my”)
-- 等价于 :
fp= (function()
--my.lua文件内容--
end)
证明 :
一个仅仅定义了一个全局变量的模块mud.lua
a = 'test mudle string '
如果require相当于以文件内容定义了一个匿名函数,那么这个文件内部的所有变量定义可见权限都应该限于这个文件所定义的这个函数中。然而 :
调用mud 主文件main.lua
requrie ("mud")
-- mud.lua 定义的a 是个全局可见的a。
print(a)
dofile 加载并执行文件
loadfile 加载不执行文件
规范的模块
table主导,不污染全局环境的模块
- 利用module函数简写
-- in module xxx.lua
-- user can change file name as he need
local ModuleName = ... -- module suit require parameter
module(ModuleName ,package.seeall)
-- all interfaces/variables are in file are in namespace ...
function TestPrint()
Print(ModuleName)
end
-- while use the module
local xxx=require("xxx")
xxx.TestPrint()
- 全部手打
local M = {};
local modelName = ...;
_G[modelName] = M; -- 保证外部使用require的参数可以引用模块内接口
function M.quit()
print("quit");
end
function M.play()
print("play");
M.quit() -- 必须写明属于 M
end
return M; -- 返回模块table ,不必须,但是支持引用者本地用自己名字保存