Lua代码编写规范
最后更新于:2022-04-01 10:07:44
## Lua代码编写规范
开发中,大量使用lua,暂时根据当前状况,总结相对而言较好的规范,在多人协作中可以更好的开发、交流。
介绍
该文档旨在为使用lua编写应用程序建立编码指南。
制订编码规范的目的:
- 统一编码标准,通用,提高开发效率;
- 使代码通俗易懂,易于维护。
切记:善用调试器。
## 一、 命名惯例
1.所有lua文件命名时使用小写字母、下划线
2.类名、变量名尽可能使用有意义的英文,类名使用[帕斯卡命名法](http://baike.baidu.com/link?url=fv2sf26sfnEy8xRFKzdmPukkM7eaCpfY-4yWy0cWr1dSq7qswARfleAb8_uTaRiREvMWspsCvZAlEwoZTgLZ4q),变量名使用[骆驼式命名法](http://baike.baidu.com/link?url=7nvqFDTUwCmzkI6fIv0vvlrfvxF__LmGyJ8RJde_5ZM-u5JL0Rl5i_RV27-whcy-Mi3Qb_AlWiIaWE7LBT43e_)
3.常量、消息号定义时用大写,单词间 _ 分割 eg:KIND_PET_FOOD
4.枚举值定义时 加前缀 enum_
5. 函数名使用骆驼式命名法
注:
骆驼式命名法:第一个单字以小写字母开始;第二个单字的首字母大写或每一个单字的首字母都采用大写字母
帕斯卡命名法:和骆驼命名法很像,只有一点区别,就是首字母要大写。(单字之间不以空格断开或连接号)
## 二、 文件组织
1. 文件开头加上此文件的功能、职责的简要描述;
如下:
--
-- Author: Feng
-- Date: XXXX-XX-XX
-- 功能描述
每个文件都加module 限定词; 导入的模块都加 local 限定词;或者使用(module(..., package.seeall)),这样便于进行热更新
2. 所有提供外部函数都加如下格式的注释。
例如:
--此函数检测是否可以从A(oldx, oldy)点走到B点(newx, newy)
--@param oldx 当前所在点x
--@param oldy 当前所在点y
--@param newx 目标点x
--@param newy 目标点y
--@return 若可以到达,返回true;否则返回false
function Object:checkBar(oldx, oldy, newx, newy)
…
end
3. 函数与函数间、以及一些定义之间加上空行。
4. 函数内的临时变量、文件内的局部函数都加上 local 限定词
5. 函数的行数过长(大于100行)时,尽量拆分为多个子函数;函数中一些晦涩的部分,一定要加上注释。
6. 短小的注释使用 --; 较长的注释使用 --[[ ]]
7. assert函数开销不小,请慎用。
8. Lua类设计时,用元表来实现oop。
不要直接增加函数成员,因为直接增加函数成员会导致内存增加并且在jit下执行效率和用元表方式无差异。
9. 文件使用UTF8格式
## 三、 分隔和缩进
1. 使用空行
在下述情况下使用单行的空白行来分隔:
1)在方法之间
2)在方法内部代码的逻辑段落小节之间
3)在注释行之前
注释之前增加一行或者多行空行。
2.使用空格符
除正常的成分之间以空格符分隔名(如数据类型和变量名之间),在下述情况下也应使用一个空格符来分隔:
1)运算符和运算符之间,如: c = a + b;
2)在参数列表中的逗号后面,如:
function m1(year, month)
end
3) 在for语句时,如:
for k, v in pairs(t) do
end
4)在下列情况下不要使用空格。
例如:
函数定义时:
function test1(a)
end
不要这样:
function test1( a )
end
函数调用时:
test1(3)
不要这样:
test1( 3 )
不要如此的原因在于:
a).容易忘记相关空格,导致风格不统一,这样还不如不加;
b).lua解析语法时是采用空格等分割来解析的,某些情况下,若不小心加空格会导致非预期的结果。
3. 使用换行符
不建议在一行中写多条语句,一条语句的长度一般超过了80个字符时,应该换行
4. 使用小括号
可以使用小括号来强行规定运算顺序
5. 使用缩进
在下述情况下应用缩进
1)类中的成分
2)方法体或语句块中的成分
3)换行时的非起始行
缩减量一般为在上一级成分的基础上跑到下一个制表位
## 四、代码建议:
1.代码中使用的一些函数尽可能在文件开头或者当前局部环境中加local前缀重新定义下。
例如:
local assert = assert
2.尽量减少表中的成员是另一个表的引用。 考虑lua的垃圾收集机制、内存泄露等。
3.高级特性尽可能不用
4.写代码时尽可能写的简单,考虑性能时先做好推断,看看能提升多少,增加的复杂度以及造成的代码晦涩有多严重,然后再决定如何做
5.加载的xml数据表,尽可能的做好数据校验,若校验失败,要出发断言,使服务器无法启动;不要等出错时,回过头来检查是数据表问题还是逻辑问题。
6.出错时,记录好错误日志。
有的函数开销比较大,而调用的频率很低,那么可以不对他做优化;
反之,有的函数开销较小,但是调用的频率很高,从如何降低调用频率以及减少函数开销两个角度去思考,然后定下优化方案
提交代码之前,去掉或者注释掉无关的代码; 测试下保证服务器可以正确启动。
我所理解lua 语言中的点、冒号与self
最后更新于:2022-04-01 10:07:42
lua编程中,经常遇到函数的定义和调用,有时候用点号调用,有时候用冒号调用,这里简单的说明一下原理。如:
### 点号调用:
~~~
-- 点号定义和点号调用:
girl = {money = 200}
function girl.goToMarket(girl ,someMoney)
girl.money = girl.money - someMoney
end
girl.goToMarket(girl ,100)
print(girl.money)
~~~
### 引用参数self:
~~~
-- 参数self指向调用者自身(类似于c++里的this 指向当前类)
girl = {money = 200}
function girl.goToMarket(self ,someMoney)
self.money = self.money - someMoney
end
girl.goToMarket(girl, 100)
print(girl.money)
~~~
### 冒号调用:
~~~
-- 冒号定义和冒号调用:
girl = {money = 200}
function girl:goToMarket(someMoney)
self.money = self.money - someMoney
end
girl:goToMarket(100)
print(girl.money)
~~~
冒号定义和冒号调用其实跟上面的效果一样,只是把第一个隐藏参数省略了,而该参数self指向调用者自身。
**总结:**冒号只是起了省略第一个参数self的作用,该self指向调用者本身,并没有其他特殊的地方。
引用博文:http://www.xuebuyuan.com/1613223.html
Lua中的模块与module函数
最后更新于:2022-04-01 10:07:39
这篇文章主要介绍了Lua中的模块(module)和包(package)详解,本文讲解了require函数、写一个模块、package.loaded、module函数等内容.
从Lua5.1版本开始,就对模块和包添加了新的支持,可是使用require和module来定义和使用模块和包。require用于使用模块,module用于创建模块。简单的说,一个模块就是一个程序库,可以通过require来加载。然后便得到了一个全局变量,表示一个table。这个table就像是一个命名空间,其内容就是模块中导出的所有东西,比如函数和常量,一个符合规范的模块还应使require返回这个table。现在就来具体的总结一下require和module这两个函数。如:
~~~
require "mod"
mod.foo()
local m2 = require "mod2"
local f = mod2.foo
f()
~~~
### 1. require函数:
require函数的调用形式为require "模块名"。该调用会返回一个由模块函数组成的table,并且还会定义一个包含该table的全局变量。在使用Lua中的标准库时可以不用显示的调用require,因为Lua已经预先加载了他们。
require函数在搜素加载模块时,有一套自定义的模式,如:
?;?.lua;c:/windows/?;/usr/local/lua/?/?.lua
在上面的模式中,只有问号(?)和分号(;)是模式字符,分别表示require函数的参数(模块名)和模式间的分隔符。如:调用require "sql",将会打开以下的文件:
sql
sql.lua
c:/windows/sql
/usr/local/lua/sql/sql.lua
Lua将require搜索的模式字符串放在变量**package.path**中。当Lua启动后,便以环境变量LUA_PATH的值来初始化这个变量。如果没有找到该环境变量,则使用一个编译时定义的默认路径来初始化。如果require无法找到与模块名相符的Lua文件,就会找C程序库。**C程序库的搜索模式存放在变量package.cpath中。**而这个变量则是通过环境变量LUA_CPATH来初始化的。
### 2. 编写模块的基本方法:
新建一个文件,命名为game.lua,代码如下:
~~~
local M = {};
local modelName = ...;
_G[modelName] = M;
function M.play()
print("那么,开始吧");
end
function M.quit()
print("你走吧,我保证你不会出事的,呵,呵呵");
end
return M;
~~~
加载game.lua,代码如下:
~~~
game = require "test"
game.play()
~~~
运行:
>lua -e "io.stdout:setvbuf 'no'" "HelloWorld.lua"
那么,开始吧
>Exit code: 0
### 3. 使用环境:
仔细阅读上例中的代码,我们可以发现一些细节上问题。比如模块内函数之间的调用仍然要保留模块名的限定符,如果是**私有变量还需要加local关键字**,**同时不能加模块名限定符。**如果需要将**私有改为公**有,或者反之,都需要一定的修改。那又该如何规避这些问题呢?我们可以通过Lua的函数“全局环境”来有效的解决这些问题。
我们把game.lua这个模块里的全局环境设置为M,于是,我们直接定义函数的时候,不需要再带M前缀。
因为此时的全局环境就是M,不带前缀去定义变量,就是全局变量,这时的全局变量是保存在M里。
所以,实际上,play和quit函数仍然是在M这个table里。
~~~
local M = {};
local modelName = ...;
_G[modelName] = M;
package.loaded[modname] = M
setfenv(1, M);
function play()
print("那么,开始吧");
end
function quit()
print("你走吧,我保证你不会出事的,呵,呵呵");
end
return M;
~~~
### 4. module函数:
在Lua 5.1中,我们可以用module(...)函数来代替以下代码,如:
~~~
local modname = ...
local M = {}
_G[modname] = M
package.loaded[modname] = M
--[[
和普通Lua程序块一样声明外部函数。
--]]
setfenv(1,M)
~~~
即是:
~~~
module(..., package.seeall);
function play()
print("那么,开始吧")
end
function quit()
print("你走吧,我保证你不会出事的,呵,呵呵");
end
~~~
由于在默认情况下,module不提供外部访问,必须在调用它之前,为需要访问的外部函数或模块声明适当的局部变量。然后Lua提供了一种更为方便的实现方式,即在调用module函数时,多传入一个package.seeall的参数,相当于 setmetatable(M, {__index = _G}) .
如:
module(...,package.seeall)
Cocos2d-x使用Luajit实现加密
最后更新于:2022-04-01 10:07:37
项目要求对lua脚本进行加密,查了一下相关的资料 ,得知lua本身可以使用luac将脚本编译为字节码(bytecode)从而实现加密,试了一下,确实可行。下面是使用原生的lua解释器编译字节码:
1、新建一个名为1.lua的文件,里面只有一句话print("Hello Lua"),新建一个空的out.lua脚本文件
2、开始--运行--cmd3、luac -o out.lua 1.lua
注: luac -o [编译后脚本名] [脚本名],必要时带上脚本路径,如:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-22_56cb2ca384efd.jpg)
[编译后脚本名] [脚本名],必要时带上脚本路径
回车之后,再打开out.lua就可以看到编译好的字节码了,如:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-22_56cb2ca3c5522.jpg)
然后实验一下,执行这个字节码脚本,可以看到lua原生的解释器可以直接解析luac编译出来的bytecode脚本,很方便!
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-22_56cb2ca3d51a6.jpg)
**重点:**做完了以上的一系列之后,我照着这个方法编译项目中的脚本,然后在cocos2dx环境下使用,发现不行!于是又查了一下资料,发现2dx使用的是luajit,lua原生编译出来的bytecode和luajit是不兼容的,所以照着上面方法编译出来的bytecode脚本无法在2dx中使用。
解决这个问题其实很简单,就是用2dx自带的luajit编译lua脚本,下面附上luajit编译bytecode的方法:
1、在cocos2d-x-2.2.3\scripting\lua\luajit\LuaJIT-2.0.1\src目录下有个msvcbuild.bat批处理文件,需要先把luajit.exe这个东西给编译出来。
2、打开visual studio的命令行工具,这个只要装了vs都会有,在安装目录里面可以找到。
3、用vs的命令行工具cd到luajit的src目录
4、执行msvcbuild.bat批处理文件,编译出luajit.exe
5、将生成的luajit.exe、lua51.dll、jit 复制到打包工具的相对目录下,这样在工具中就可以直接调用luajit –b source_file out_file (一般都是lua后缀,代码不用改动)
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-22_56cb2ca3e5fd0.jpg)
接下来就可以使用luajit.exe编译lua脚本的bytecode了:luajit -b [脚本名] [编译后的脚本名],执行完后会在src目录下生成一个已经编译成bytecode的jit.lua文件。
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-22_56cb2ca40043a.jpg)
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-22_56cb2ca41714f.jpg)
下面把编译之后的jit.lua放在2dx中试一下,以HelloLua工程为基础,把jit.lua放到\samples\Lua\HelloLua\Resources下,修改AppDelegate.cpp中的lua调用为std::string path = CCFileUtils::sharedFileUtils()->fullPathForFilename("jit.lua");运行结果为:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-22_56cb2ca430fb0.jpg)
至此,luajit编译bytecode加密已完成!
**严重注意:**例子中,我把编译前后的脚本名字取的不一样,是为了让大家看出差异化来,实际在项目中使用的时候,脚本的名字编译前后最好都一致,不然在脚本中相互require的时候可能会出现问题!一个一个转换脚太麻烦了,分享一个bat批处理,可以批量转换一个文件夹中的所有lua文件.
代码如下:
~~~
@echo off
if exist out rd /s /q out
mkdir out
:input
cls
set input=:
set /p input= 拖入要编译的lua文件夹:
set "input=%input:"=%"
if "%input%"==":" goto input
if not exist "%input%" goto input
for %%i in ("%input%") do if /i "%%~di"==%%i goto input
pushd %cd%
cd /d "%input%">nul 2>nul || exit
set cur_dir=%cd%
popd
set /a num = 0
for /f "delims=" %%i in ('dir /b /a-d /s "%input%"') do (set /a num += 1 & luajit -b %%~fsi out/%%~nxi & echo %%~nxi)
echo 编译脚本数量:%num%
ATTRIB out/*.* +R
pause
~~~
编译后,文件夹内所有的lua脚本将被批量编译为字节码,并保存在xxx\out目录下,如:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-22_56cb2ca44a461.jpg)
注:XXX为打包加密文件路径
还有小提示:ios64目前只支持lua,不支持用luajit生成二进制*.lua.
引用博客:http://jingyan.baidu.com/article/0a52e3f4179713bf62ed72f1.html
lua 函数回调技巧
最后更新于:2022-04-01 10:07:35
## 技巧1:
~~~
local a = {};
function b()
print("Hello World")
end
a["sell"] = {callFunc =b}
a["sell"].callFunc()
~~~
#
## 技巧2:
使用lua 自带的 unpack :
解释:把一直数组(只有连续数字下标的 table)展开成一串返回值,但是对用字符串或别的东西做 key 的 table 无能为力。
~~~
function unpackex(tbl, args)
local ret = {}
for _,v in ipairs(args) do
table.insert(ret, tbl[v])
end
return unpack(ret)
end
print(unpackex({one = {"one", "two", "three"}, two = "T" , three = "TH"},{"one", "two", "three"}))
~~~
输出:>> table: 00ABC2D0TTH
Lua中的元表与元方法
最后更新于:2022-04-01 10:07:32
## 前言
Lua中每个值都可具有元表。 元表是普通的Lua表,定义了原始值在某些特定操作下的行为。你可通过在值的原表中设置特定的字段来改变作用于该值的操作的某些行为特征。
例如,当数字值作为加法的操作数时,Lua检查其元表中的"__add"字段是否有个函数。如果有,Lua调用它执行加法。
我们称元表中的键为事件(event),称值为元方法(metamethod)。前述例子中的事件是"add",元方法是执行加法的函数。
可通过函数getmetatable查询任何值的元表。
在table中,我可以重新定义的元方法有以下几个:
~~~
__add(a, b) --加法
__sub(a, b) --减法
__mul(a, b) --乘法
__div(a, b) --除法
__mod(a, b) --取模
__pow(a, b) --乘幂
__unm(a) --相反数
__concat(a, b) --连接
__len(a) --长度
__eq(a, b) --相等
__lt(a, b) --小于
__le(a, b) --小于等于
__index(a, b) --索引查询
__newindex(a, b, c) --索引更新(PS:不懂的话,后面会有讲)
__call(a, ...) --执行方法调用
__tostring(a) --字符串输出
__metatable --保护元表
~~~
Lua中的每一个表都有其Metatable。Lua默认创建一个不带metatable的新表
~~~
t = {}
print(getmetatable(t)) --> nil
~~~
可以使用setmetatable函数设置或者改变一个表的metatable
~~~
t1 = {}
setmetatable(t, t1)
assert(getmetatable(t) == t1)
~~~
任何一个表都可以是其他一个表的metatable,一组相关的表可以共享一个metatable(描述他们共同的行为)。一个表也可以是自身的metatable(描述其私有行为)。
接下来就介绍介绍如果去重新定义这些方法。
## 算术类的元方法
现在我使用完整的实例代码来详细的说明算术类元方法的使用。
~~~
Set = {}
local mt = {} -- 集合的元表
-- 根据参数列表中的值创建一个新的集合
function Set.new(l)
local set = {}
setmetatable(set, mt)
for _, v in pairs(l) do set[v] = true end
return set
end
-- 并集操作
function Set.union(a, b)
local retSet = Set.new{} -- 此处相当于Set.new({})
for v in pairs(a) do retSet[v] = true end
for v in pairs(b) do retSet[v] = true end
return retSet
end
-- 交集操作
function Set.intersection(a, b)
local retSet = Set.new{}
for v in pairs(a) do retSet[v] = b[v] end
return retSet
end
-- 打印集合的操作
function Set.toString(set)
local tb = {}
for e in pairs(set) do
tb[#tb + 1] = e
end
return "{" .. table.concat(tb, ", ") .. "}"
end
function Set.print(s)
print(Set.toString(s))
end
~~~
现在,我定义“+”来计算两个集合的并集,那么就需要让所有用于表示集合的table共享一个元表,并且在该元表中定义如何执行一个加法操作。首先创建一个常规的table,准备用作集合的元表,然后修改Set.new函数,在每次创建集合的时候,都为新的集合设置一个元表。代码如下:
~~~
Set = {}
local mt = {} -- 集合的元表
-- 根据参数列表中的值创建一个新的集合
function Set.new(l)
local set = {}
setmetatable(set, mt)
for _, v in pairs(l) do set[v] = true end
return set
end
~~~
在此之后,所有由Set.new创建的集合都具有一个相同的元表,例如:
~~~
local set1 = Set.new({10, 20, 30})
local set2 = Set.new({1, 2})
print(getmetatable(set1))
print(getmetatable(set2))
assert(getmetatable(set1) == getmetatable(set2))
~~~
最后,我们需要把元方法加入元表中,代码如下:
~~~
mt.__add = Set.union
~~~
这以后,只要我们使用“+”符号求两个集合的并集,它就会自动的调用Set.union函数,并将两个操作数作为参数传入。比如以下代码:
~~~
local set1 = Set.new({10, 20, 30})
local set2 = Set.new({1, 2})
local set3 = set1 + set2
Set.print(set3)
~~~
在上面列举的那些可以重定义的元方法都可以使用上面的方法进行重定义。现在就出现了一个新的问题,set1和set2都有元表,那我们要用谁的元表啊?虽然我们这里的示例代码使用的都是一个元表,但是实际coding中,会遇到我这里说的问题,对于这种问题,Lua是按照以下步骤进行解决的:
1. 对于二元操作符,如果第一个操作数有元表,并且元表中有所需要的字段定义,比如我们这里的__add元方法定义,那么Lua就以这个字段为元方法,而与第二个值无关;
1. 对于二元操作符,如果第一个操作数有元表,但是元表中没有所需要的字段定义,比如我们这里的__add元方法定义,那么Lua就去查找第二个操作数的元表;
1. 如果两个操作数都没有元表,或者都没有对应的元方法定义,Lua就引发一个错误。
以上就是Lua处理这个问题的规则,那么我们在实际编程中该如何做呢?
比如set3 = set1 + 8这样的代码,就会打印出以下的错误提示:
~~~
lua: test.lua:16: bad argument #1 to 'pairs' (table expected, got number)
~~~
但是,我们在实际编码中,可以按照以下方法,弹出我们定义的错误消息,代码如下:
~~~
function Set.union(a, b)
if getmetatable(a) ~= mt or getmetatable(b) ~= mt then
error("metatable error.")
end
local retSet = Set.new{} -- 此处相当于Set.new({})
for v in pairs(a) do retSet[v] = true end
for v in pairs(b) do retSet[v] = true end
return retSet
end
~~~
当两个操作数的元表不是同一个元表时,就表示二者进行并集操作时就会出现问题,那么我们就可以打印出我们需要的错误消息。
上面总结了算术类的元方法的定义,关系类的元方法和算术类的元方法的定义是类似的,这里不做累述。
## __tostring元方法
写过Java或者C#的人都知道,Object类中都有一个tostring的方法,程序员可以重写该方法,以实现自己的需求。在Lua中,也是这样的,当我们直接print(a)(a是一个table)时,是不可以的。那怎么办,这个时候,我们就需要自己重新定义__tostring元方法,让print可以格式化打印出table类型的数据。
函数print总是调用tostring来进行格式化输出,当格式化任意值时,tostring会检查该值是否有一个__tostring的元方法,如果有这个元方法,tostring就用该值作为参数来调用这个元方法,剩下实际的格式化操作就由__tostring元方法引用的函数去完成,该函数最终返回一个格式化完成的字符串。例如以下代码:
~~~
mt.__tostring = Set.toString
~~~
## 如何保护我们的“奶酪”——元表
我们会发现,使用getmetatable就可以很轻易的得到元表,使用setmetatable就可以很容易的修改元表,那这样做的风险是不是太大了,那么如何保护我们的元表不被篡改呢?在Lua中,函数setmetatable和getmetatable函数会用到元表中的一个字段,用于保护元表,该字段是__metatable。当我们想要保护集合的元表,是用户既不能看也不能修改集合的元表,那么就需要使用__metatable字段了;当设置了该字段时,getmetatable就会返回这个字段的值,而setmetatable则会引发一个错误;如以下演示代码:
~~~
function Set.new(l)
local set = {}
setmetatable(set, mt)
for _, v in pairs(l) do set[v] = true end
mt.__metatable = "You cannot get the metatable" -- 设置完我的元表以后,不让其他人再设置
return set
end
local tb = Set.new({1, 2})
print(tb)
print(getmetatable(tb))
setmetatable(tb, {})
~~~
上述代码就会打印以下内容:
~~~
{1, 2}
You cannot get the metatable
lua: test.lua:56: cannot change a protected metatable
~~~
## __index元方法
是否还记得当我们访问一个table中不存在的字段时,会返回什么值?默认情况下,当我们访问一个table中不存在的字段时,得到的结果是nil。但是这种状况很容易被改变;Lua是按照以下的步骤决定是返回nil还是其它值得:
1. 当访问一个table的字段时,如果table有这个字段,则直接返回对应的值;
1. 当table没有这个字段,则会促使解释器去查找一个叫__index的元方法,接下来就就会调用对应的元方法,返回元方法返回的值;
1. 如果没有这个元方法,那么就返回nil结果。
下面通过一个实际的例子来说明__index的使用。假设要创建一些描述窗口,每个table中都必须描述一些窗口参数,例如颜色,位置和大小等,这些参数都是有默认值得,因此,我们在创建窗口对象时可以指定那些不同于默认值得参数。
~~~
Windows = {} -- 创建一个命名空间
-- 创建默认值表
Windows.default = {x = 0, y = 0, width = 100, height = 100, color = {r = 255, g = 255, b = 255}}
Windows.mt = {} -- 创建元表
-- 声明构造函数
function Windows.new(o)
setmetatable(o, Windows.mt)
return o
end
-- 定义__index元方法
Windows.mt.__index = function (table, key)
return Windows.default[key]
end
local win = Windows.new({x = 10, y = 10})
print(win.x) -- >10 访问自身已经拥有的值
print(win.width) -- >100 访问default表中的值
print(win.color.r) -- >255 访问default表中的值
~~~
根据上面代码的输出,结合上面说的那三步,我们再来看看,print(win.x)时,由于win变量本身就拥有x字段,所以就直接打印了其自身拥有的字段的值;print(win.width),由于win变量本身没有width字段,那么就去查找是否拥有元表,元表中是否有__index对应的元方法,由于存在__index元方法,返回了default表中的width字段的值,print(win.color.r)也是同样的道理。
在实际编程中,__index元方法不必一定是一个函数,它还可以是一个table。当它是一个函数时,Lua以table和不存在key作为参数来调用该函数,这就和上面的代码一样;当它是一个table时,Lua就以相同的方式来重新访问这个table,所以上面的代码也可以是这样的:
~~~
-- 定义__index元方法
Windows.mt.__index = Windows.default
~~~
## __newindex元方法
__newindex元方法与__index类似,__newindex用于更新table中的数据,而__index用于查询table中的数据。当对一个table中不存在的索引赋值时,在Lua中是按照以下步骤进行的:
Lua解释器先判断这个table是否有元表;
1. 如果有了元表,就查找元表中是否有__newindex元方法;如果没有元表,就直接添加这个索引,然后对应的赋值;
1. 如果有这个__newindex元方法,Lua解释器就执行它,而不是执行赋值;
1. 如果这个__newindex对应的不是一个函数,而是一个table时,Lua解释器就在这个table中执行赋值,而不是对原来的table。
那么这里就出现了一个问题,看以下代码:
~~~
local tb1 = {}
local tb2 = {}
tb1.__newindex = tb2
tb2.__newindex = tb1
setmetatable(tb1, tb2)
setmetatable(tb2, tb1)
tb1.x = 10
~~~
发现什么问题了么?是不是循环了,在Lua解释器中,对这个问题,就会弹出错误消息,错误消息如下:
~~~
loop in settable
~~~
引用博客:http://www.jellythink.com/archives/511
lua 如何输出树状结构的table?
最后更新于:2022-04-01 10:07:30
为了让游戏前端数据输出更加条理,做了一个简单树状结构来打印数据。
ccmlog.lua
~~~
local function __tostring(value, indent, vmap)
local str = ''
indent = indent or ''
vmap = vmap or {}
--递归结束条件
if (type(value) ~= 'table') then
if (type(value) == 'string') then
--字符串
str = string.format("[[%s]]", value)
else
--整数
str = tostring(value)
end
else
if type(vmap) == 'table' then
if vmap[value] then return '('..tostring(value)..')' end
vmap[value] = true
end
local auxTable = {} --保存元表KEY(非整数)
local iauxTable = {} --保存元表value
local iiauxTable = {} --保存数组(key为0)
table.foreach(value, function(i, v)
if type(i) == 'number' then
if i == 0 then
table.insert(iiauxTable, i)
else
table.insert(iauxTable, i)
end
elseif type(i) ~= 'table' then
table.insert(auxTable, i)
end
end)
table.sort(iauxTable)
str = str..'{\n'
local separator = ""
local entry = "\n"
local barray = true
local kk,vv
table.foreachi (iauxTable, function (i, k)
if i == k and barray then
entry = __tostring(value[k], indent..' \t', vmap)
str = str..separator..indent..' \t'..entry
separator = ", \n"
else
barray = false
table.insert(iiauxTable, k)
end
end)
table.sort(iiauxTable)
table.foreachi (iiauxTable, function (i, fieldName)
kk = tostring(fieldName)
if type(fieldName) == "number" then
kk = '['..kk.."]"
end
entry = kk .. " = " .. __tostring(value[fieldName],indent..' \t',vmap)
str = str..separator..indent..' \t'..entry
separator = ", \n"
end)
table.sort(auxTable)
table.foreachi (auxTable, function (i, fieldName)
kk = tostring(fieldName)
if type(fieldName) == "number" then
kk = '['..kk.."]"
end
vv = value[fieldName]
entry = kk .. " = " .. __tostring(value[fieldName],indent..' \t',vmap)
str = str..separator..indent..' \t'..entry
separator = ", \n"
end)
str = str..'\n'..indent..'}'
end
return str
end
ccmlog = function(m,fmt,...)
local args = {...}
for k,arg in ipairs(args) do
if type(arg) == 'table'
or type(arg) == 'boolean'
or type(arg) == 'function'
or type(arg) == 'userdata' then
args[k] = __tostring(arg)
end
end
args[#args+1] = "nil"
args[#args+1] = "nil"
args[#args+1] = "nil"
local str = string.format("[%s]:"..fmt.." %s", m, unpack(args))
print(str)
local off = 1
local p = CCLOGWARN
if m == 'error' then
p = CCLOGERROR
elseif m == 'warn' then
p = CCLOGWARN
end
while off <= #str do
local subStr = string.sub(str, off, off+1024)
off = off + #subStr
--p(subStr)
end
end
--打印测试
reserved = { [100] = { 300, 400}, 200, { 300, 500}, abc = "abc",[0] = {1,2,3,"abc"}}
ccmlog("d","d",reserved)
~~~
打印效果如下:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-22_56cb2ca36f718.jpg)
为了让程序模块调用该打印接口,打印函数前加上module("ccmlog", package.seeall),并将此文件保存在类似lualibs的库目录,然后在程序中用require "ccmlog"来调用该函数了.
Lua 常用数据结构
最后更新于:2022-04-01 10:07:28
Lua中的table不是一种简单的数据结构,它可以作为其它数据结构的基础。如数组、记录、线性表、队列和集合等,在Lua中都可以通过table来表示。
## 一、数组
在lua中通过整数下标访问表中的元素即可简单的实现数组。并且数组不必事先指定大小,大小可以随需要动态的增长。
~~~
a = {}
for i = 1,100 do
a[i] = 0
end
print("The length of array 'a' is " .. #a)
squares = {1, 4, 9, 16, 25}
print("The length of array 'a' is " .. #squares)
~~~
在Lua中习惯上数组的下表从1开始,Lua的标准库与此习惯保持一致,因此如果你的数组下标也是从1开始你就可以直接使用标准库的函数,否则就无法直接使用。
## 二、二维数组
Lua中主要有两种表示矩阵的方法,第一种是用数组的数组表示。也就是说一个表的元素是另一个表。
~~~
local N = 3
local M = 3
mt = {}
for i = 1,N do
mt[i] = {}
for j = 1,M do
mt[i][j] = i * j
end
end
mt = {}
for i = 1, N do
for j = 1, M do
mt[(i - 1) * M + j] = i * j
end
end
~~~
#
## 三、链表
Lua中用tables很容易实现链表,每一个节点是一个table,指针是这个表的一个域,并且指向另一个节点(table)。例如,要实现一个只有两个域:值和指针的基本链表,代码如下:
~~~
list = nil
for i = 1, 10 do
list = { next = list ,value = i}
end
local l = list
while l do
--print(l.value)
l = l.next
end
~~~
## 四、队列与双向队列
虽然可以使用Lua的table库提供的insert和remove操作来实现队列,但这种方式实现的队列针对大数据量时效率太低,有效的方式是使用两个索引下标,一个表示第一个元素,另一个表示最后一个元素。
~~~
List = {}
--创建
function List.new()
return {first = 0,last = -1}
end
--队列头插入
function List.pushFront(list,value)
local first = list.first - 1
list.first = first
list[first] = value
end
--队列尾插入
function List.popFront(list)
local first = list.first
if first > list.last then
error("List is empty")
end
local value = list[first]
list[first] = nil
list.first = first + 1
return value
end
function List.popBack(list)
local last = list.last
if list.first > last then
error("List is empty")
end
local value = list[last]
list[last] = nil
list.last = last - 1
return value
end
--测试代码
local testList = {first = 0,last = -1}
local tableTest = 12
List.pushFront(testList,tableTest)
print( List.popFront(testList))
~~~
## 五、栈
简单实现堆栈功能,代码如下:
~~~
local stackMng = {}
stackMng.__index = stackMng
function stackMng:new()
local temp = {}
setmetatable(temp,stackMng)
return temp
end
function stackMng:init()
self.stackList = {}
end
function stackMng:reset()
self:init()
end
function stackMng:clear()
self.stackList = {}
end
function stackMng:pop()
if #self.stackList == 0 then
return
end
if self.stackList[1] then
print(self.stackList[1])
end
return table.remove(self.stackList,1)
end
function stackMng:push(t)
table.insert(self.stackList,t)
end
function stackMng:Count()
return #self.stackList
end
--测试代码
object = stackMng:new()
object:init()
object:push(1)
object:pop()
~~~
## 六、集合
在Lua中用table实现集合是非常简单的,见如下代码:
~~~
reserved = {
["while"] = true, ["end"] = true,
["function"] = true, ["local"] = true,
}
for k,v in pairs(reserved) do
print(k,"->",v)
end
~~~
Lua中调用C函数(lua-5.2.3)
最后更新于:2022-04-01 10:07:26
Lua可以调用C函数的能力将极大的提高Lua的可扩展性和可用性。
对于有些和操作系统相关的功能,或者是对效率要求较高的模块,我们完全可以通过C函数来实现,之后再通过Lua调用指定的C函数。
对于那些可被Lua调用的C函数而言,其接口必须遵循Lua要求的形式,即typedef int (*lua_CFunction)(lua_State* L)。
简单说明一下,该函数类型仅仅包含一个表示Lua环境的指针作为其唯一的参数,实现者可以通过该指针进一步获取Lua代码中实际传入的参数。返回值是整型,表示该C函数将返回给Lua代码的返回值数量,如果没有返回值,则return 0即可。需要说明的是,C函数无法直接将真正的返回值返回给Lua代码,而是通过虚拟栈来传递Lua代码和C函数之间的调用参数和返回值的。
**实例代码:**
~~~
// testlua.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include <stdio.h>
#include <string.h>
#include <math.h>
extern "C"
{
#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>
}
//待Lua调用的C注册函数
static int add2(lua_State* L)
{
//检查栈中的参数是否合法,1表示Lua调用时的第一个参数(从左到右),依此类推。
//如果Lua代码在调用时传递的参数不为number,该函数将报错并终止程序的执行。
double op1 = luaL_checknumber(L,1);
double op2 = luaL_checknumber(L,2);
//将函数的结果压入栈中。如果有多个返回值,可以在这里多次压入栈中。
lua_pushnumber(L,op1 + op2);
//返回值用于提示该C函数的返回值数量,即压入栈中的返回值数量。
return 1;
}
//待Lua调用的C注册函数。
static int sub2(lua_State* L)
{
double op1 = luaL_checknumber(L,1);
double op2 = luaL_checknumber(L,2);
lua_pushnumber(L,op1 - op2);
return 1;
}
//待Lua调用的C注册函数。
static int l_sin (lua_State *L) {
double d = lua_tonumber(L, 1); /* get argument */
lua_pushnumber(L, sin(d)); /* push result */
return 1; /* number of results */
}
int _tmain(int argc, _TCHAR* argv[])
{
lua_State *L = luaL_newstate();
luaL_openlibs(L);
//将指定的函数注册为Lua的全局函数变量,其中第一个字符串参数为Lua代码
//在调用C函数时使用的全局函数名,第二个参数为实际C函数的指针。
lua_register(L, "add2", add2);
lua_register(L, "sub2", sub2);
lua_register(L, "l_sin", l_sin);
//在注册完所有的C函数之后,即可在Lua的代码块中使用这些已经注册的C函数了。
luaL_dofile(L,"test.lua");
//if (luaL_dostring(L,testfunc))
// printf("Failed to invoke.\n");
//const char *buf = "print('Hello World')";
//luaL_dostring(L,buf);
lua_close(L);
return 0;
}
~~~
test.lua
~~~
function show()
print("helloworld")
print(add2(1.0,2.0))
print(sub2(20.1,19))
print(l_sin(1))
end
show()
~~~
运行结果:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-22_56cb2ca3597e4.jpg)
引用博文:http://www.cnblogs.com/stephen-liu74/archive/2012/07/23/2469902.html
VS2010编译Lua程序(lua-5.2.3)
最后更新于:2022-04-01 10:07:23
## 编译静态链接库
1.下载[Lua](http://www.lua.org/download.html)源码
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-22_56cb2ca316491.jpg)
下载后解压到一个目录下,这里假设解压到D:\win32Lua 注意下载的版本,如果是5.2.x,后面代码中的C API发生了改变
2)在VS2010中新建一个静态库项目,项目命名为lua
a 选择新建 Win32 console project
b 在wizard界面选择 static Library;不选择Precomplied Header
3)往工程中添加代码
a 复制D:\win32Lua\lua-5.2.3\src 目录下的*.h文件到项目的Header Files目录下
b 复制D:\win32Lua\lua-5.2.3\src 目录下的*.c文件到项目的Code Files目录下
注:需要注意的是 lua.c 和luac.c 不能一起编译进去。
4)配置项目的属性,在项目的“配置属性” 界面中操作
a Configuration Properties -> C/C++-> General -> Additional Include Directories
添加D:\win32Lua\lua-5.2.3\src
b Configuration Properties -> C/C++-> Advanced -> compile as
这里的选择将影响后面代码中如何指定编译链接方式,后面的测试选择的是Compile as C code
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-22_56cb2ca3283cf.jpg)
5)生产项目 Build
如果是DEBUG mode 将在Debug目录下看到一个lua.lib文件,Release mode的lib文件在Release文件下
## C/C++代码中调用lua
1)在解决方案中添加一个 Win32 console project,项目名称命名为testlua,后面wizard界面中的选项无需修改
2)添加对lua项目的引用
a Common Properties -> Framework and References -> Add New References
选择lua项目
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-22_56cb2ca339e55.jpg)
3)添加对头文件的include directory
a Configuration Properties -> C/C++-> General -> Additional Include Directories
添加D:\win32Lua\lua-5.2.3\src
## 示例代码:
~~~
// testlua.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include <stdio.h>
#include <string.h>
extern "C"
{
#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>
}
int _tmain(int argc, _TCHAR* argv[])
{
lua_State *L = luaL_newstate();
luaL_openlibs(L);
luaL_dofile(L,"test.lua");
//const char *buf = "print('Hello World')";
//luaL_dostring(L,buf);
lua_close(L);
return 0;
}
~~~
test.lua
~~~
function show()
print("helloworld")
end
show()
~~~
运行效果:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-22_56cb2ca3498ea.jpg)
引用博文:http://blog.csdn.net/berdy/article/details/7925040
LUA IO库
最后更新于:2022-04-01 10:07:21
I/O库为文件操作提供两种模式。简单模式(simple model)拥有一个当前输入文件和一个当前输出文件,并且提供针对这些文件相关的操作。完全模式(complete model)使用外部的文件句柄来实现。
## 简单模式
I/O库将当前输入文件作为标准输入(stdin),将当前输出文件作为标准输出(stdout)。这样当我们执行io.read,就是在标准输入中读取一行。
写操作较读操作简单,我们先从写操作入手。
下面这个例子里函数io.write获取任意数目的字符串参数,接着将它们写到当前的输出文件。
~~~
local t = io.write("sin (3) = ", math.sin(3), "\n")
--> sin (3) = 0.1411200080598672
print("hello", "Lua"); print("Hi")
-->hello Lua
-->Hi
~~~
注:Write函数与print函数不同在于,write不附加任何额外的字符到输出中去,例如制表符,换行符等等。还有write函数是使用当前输出文件,而print始终使用标准输出。另外print函数会自动调用参数的tostring方法,所以可以显示出表(tables)函数(functions)和nil。
read函数:从当前输入文件读取串,由它的参数控制读取的内容:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-22_56cb2ca2ad621.jpg)
例子:
~~~
--io.read 从标准输入流中获得,默认设置下,就是你的屏幕输入
t = io.read("*all")
t = string.gsub(t, ...) -- do the job
io.write(t) -- write the
~~~
提示:若使用luaEditor编辑器,估计无法在屏幕输入。
## 完全模式
完全模式的核心在于文件句柄(file handle)。该结构类似于C语言中的文件流(FILE*),其呈现了一个打开的文件以及当前存取位置。打开一个文件的函数是io.open。它模仿C语言中的fopen函数,同样需要打开文件的文件名参数,打开模式的字符串参数:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-22_56cb2ca2bc20e.jpg)
例子:
~~~
--读操作
file = io.open("testRead.txt", "r")
for line in file:lines() do
print(line)
end
file:close()
--写操作
file = io.open("testRead.txt","a+")
file:write("\nhello")
file:close()
~~~
素材:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-22_56cb2ca2cc9ea.jpg)
内容:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-22_56cb2ca2dec7b.jpg)
运行结果:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-22_56cb2ca2ed7b8.jpg)
文件内容:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-22_56cb2ca30537e.jpg)
LUA string库
最后更新于:2022-04-01 10:07:19
Lua解释器对字符串的支持很有限。一个程序可以创建字符串并连接字符串,但不能截取子串,检查字符串的大小,检测字符串的内容。在Lua中操纵字符串的功能基本来自于string库。
## 一、String库的常用函数:
~~~
--返回字符串s的长度
local s = "HelloWorld"
print(string.len(s)) -->10
--重复n次字符串s的串
print(string.rep(s,2)) -->HelloWorldHelloWorld
--大写字母转换成小写
print(string.lower(s)) -->helloworld
--小写转换成大写
print(string.upper(s)) -->HELLOWORLD
--截取字符串
local s = "[in brackets]"
print(string.sub(s,2,-1)) -->in brackets]
--将每一个数字转换成字符
print(string.char(97)) -->a
--将每一个字符转换成数字
print(string.byte("abc"))
print(string.byte("abc", 2)) --> 98
print(string.byte("abc", -1)) --> 99
--注:使用负数索引访问字符串的最后一个字符
--对字符串进行格式化输出
PI = 3.14165120
print(string.format("pi = %.4f", PI)) -->pi = 3.1417
--注释:使用和C语言的printf函数几乎一模一样,你完全可以照C语言的printf来使用这个函数.
~~~
注:
string库中所有的字符索引从前往后是1,2,...;从后往前是-1,-2,...
string库中所有的function都不会直接操作字符串,而是返回一个结果。
## 二、String库的模式匹配函数
在string库中功能最强大的函数是:string.find(字符串查找),string.gsub(全局字符串替换),and string.gfind(全局字符串查找)。这些函数都是基于模式匹配的。
**1.string.find **
说明:用来在目标串(subject string)内搜索匹配指定的模式的串。函数如果找到匹配的串返回他的位置,否则返回nil.最简单的模式就是一个单词,仅仅匹配单词本身。比如,模式'hello'仅仅匹配目标串中的"hello"。当查找到模式的时候,函数返回两个值:匹配串开始索引和结束索引。
~~~
local s = "hello world"
i,j = string.find(s,"hello")
print(i," ",j) -->1 5
print(string.find(s, "kity")) -->nil
~~~
string.find函数第三个参数是可选的:标示目标串中搜索的起始位置。当我们想查找目标串中所有匹配的子串的时候,这个选项非常有用。
~~~
local s = "\nare you ok!\n OK\n"
local t = {}
local i = 0
while true do
i = string.find(s,"\n",i+1)
if i == nil then break end
table.insert(t,i)
end
for k,v in pairs(t) do
print(k,"->",v)
end
~~~
运行结果:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-22_56cb2ca25a77f.jpg)
**2.string.gsub**
说明:函数有三个参数:目标串,模式串,替换串。他基本作用是用来查找匹配模式的串,并将使用替换串其替换掉:
~~~
s = string.gsub("Lua is cute", "cute", "great")
print(s) -->Lua is great
--第四个参数是可选的,用来限制替换的范围
s = string.gsub("all lii", "l", "x", 1)
print(s) -->axl lii
_, count = string.gsub("all lii", "l", "x", 2)
print(count) -->2
~~~
注:_ 只是一个哑元变量
**3.string.gfind **
说明:遍历一个字符串内所有匹配模式的子串。如:
~~~
--收集一个字符串中所有的单词,然后插入到一个表中:
local s = "hello hi, again"
words = {}
for w in string.gfind(s, "(%a)") do
table.insert(words, w)
end
for k, v in pairs(words) do
print(k,v)
end
~~~
运行效果:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-22_56cb2ca26a04a.jpg)
## 三、模式
字符类指可以匹配一个特定字符集合内任何字符的模式项。比如,字符类%d匹配任意数字。
~~~
s = "Deadline is 30/05/1999, firm"
date = "%d%d/%d%d/%d%d%d%d"
print(string.sub(s, string.find(s, date))) --> 30/05/1999
~~~
下面的表列出了Lua支持的所有字符类:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-22_56cb2ca27aaa8.jpg)
在模式匹配中有一些特殊字符,他们有特殊的意义,Lua中的特殊字符如下:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-22_56cb2ca28a1ec.jpg)
例子:
~~~
print(string.gsub("hello, A up-down!", "%A", ".")) -->hello..A.up.down. 5
--参数4不是字符串结果的一部分,他是gsub返回的第二个结果,代表发生替换的次数
--[]方括号将字符类或者字符括起来创建自己的字符类
print(string.gsub("text", "[AEIOUaeiou]", "")) -->txt 1
print(string.len("one, and two; and three")) -->23
print(string.gsub("one123, and two", "%d+", "XXX")) -->oneXXX, and two 1
~~~
#
## 四、捕获
Capture3是这样一种机制:可以使用模式串的一部分匹配目标串的一部分。将你想捕获的模式用圆括号括起来,就指定了一个capture。
在string.find使用captures的时候,函数会返回捕获的值作为额外的结果。这常被用来将一个目标串拆分成多个:
~~~
pair = "name = Anna"
a, b, key, value = string.find(pair, "(%a+)%s*=%s*(%a+)")
print(a," ",b," ",key," ",value) --> 1 11 name Anna
~~~
'%a+' 表示非空的字母序列;'%s*' 表示0个或多个空白。在上面的例子中,整个模式代表:一个字母序列,后面是任意多个空白,然后是 '=' 再后面是任意多个空白,然后是一个字母序列。两个字母序列都是使用圆括号括起来的子模式,当他们被匹配的时候,他们就会被捕获。当匹配发生的时候,find函数总是先返回匹配串的索引下标,然后返回子模式匹配的捕获部分。
我们常常需要使用string.gsub遍历字符串,而对返回结果不感兴趣。比如,我们收集一个字符串中所有的单词,然后插入到一个表中:
~~~
--收集一个字符串中所有的单词,然后插入到一个表中:
local s = "hello hi, again"
words = {}
for w in string.gfind(s, "(%a)") do
table.insert(words, w)
end
for k, v in pairs(words) do
print(k,"->",v)
end
~~~
运行结果:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-22_56cb2ca29b41c.jpg)
Lua中的常用操作系统库
最后更新于:2022-04-01 10:07:16
## os.time ([table])
功能:按table的内容返回一个时间值(数字),若不带参数则返回当前时间.(在许多系统中该数值是当前距离某个特定时间的秒数。)
说明:当为函数调用附加一个特殊的时间表时,该函数就是返回距该表描述的时间的数值。这样的时间表有如下的区间:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-22_56cb2ca1cd136.jpg)
前三项是必需的,如果未定义后几项,默认时间为正午(12:00:00)。如果是在里约热内卢(格林威治向西三个时区)的一台Unix计算机上(相对时间为1970年1月1日,00:00:00),对于pc机(中国时区而言)有稍微更改,更改了为1970年1月1日,08:00:00,这是因我国与其它国家时间差导致。
例子:
~~~
print(os.time{year=1970, month=1, day=1,hour=8})
print(os.time{year=1970, month=1, day=1}) --若未定义“时,分,秒”,默认时间为正午(04:00:00)
~~~
运行结果:
-->0
-->14400(14400 = 4*60*60 )
## os.date ([format [, time]])
功能:返回一个按format格式化日期、时间的字串或表
说明:函数date,其实是time函数的一种“反函数”。它将一个表示日期和时间的数值,转换成更高级的表现形式。其第一个参数是一个格式化字符串,描述了要返回的时间形式。第二个参数就是时间的数字表示,默认为当前的时间。
参数:format:
*t":将返一个带year(4位),month(1-12), day (1--31), hour (0-23), min (0-59), sec (0-61), wday (星期几, 星期天为1), yday (年内天数), and isdst (是否为日光节约时间true/false)的带键名的表;
若没有"*t"则返回一个按C的strftime函数格式化的字符串;
若不带参数,则按当前系统的设置返回格式化的字符串 os.date() <=> os.date("%c")
例子:我当前PC时间,如图:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-22_56cb2ca1d9efb.jpg)
代码:
~~~
t = os.date("*t", os.time());
for i, v in pairs(t) do
print(i,"->",v);
end
~~~
运行结果 :
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-22_56cb2ca1ec24b.jpg)
运行结果和以上时钟的秒,不一致,你想,截图也要时间的,呵呵。
如果使用带标记(见下表)的特殊字符串,os.data函数会将相应的标记位以时间信息进行填充,得到一个包含时间的字符串。
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-22_56cb2ca206e5b.jpg)
例子:
~~~
print(os.date("today is %A, in %B"))
print(os.date("%X", 906000490))
~~~
运行结果:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-22_56cb2ca21c99c.jpg)
同时,也可以使用明确的字符串格式方式(例如"%m/%d/%Y")
例子:
~~~
print(os.date("%m/%d/%Y", 906000490))
~~~
运行结果:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-22_56cb2ca22bfdc.jpg)
## os.difftime (t2, t1)
功能:返回t1到t2相差的秒数
例子:
~~~
t1 = os.time();
for i = 0, 100000 do
os.time();
end
t2 = os.time();
print(string.format("t1: %d t2: %d",t1,t2))
print(os.date("%x", t1))
print(os.date("%X", t2))
print(os.difftime(t2, t1));
~~~
运行结果:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-22_56cb2ca239fe3.jpg)
## os.clock ()
功能:返回一个程序使用CPU时间的一个近似值
例子:
~~~
local x = os.clock();
print(os.clock())
local s = 0;
for i = 1, 100000 do
s = s + i;
end
print(string.format("elapsed time : %.2f\n", os.clock() - x));
~~~
运行结果:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-22_56cb2ca247ca5.jpg)
Lua中的table函数库
最后更新于:2022-04-01 10:07:14
## Lua中的table函数库
table库由一些操作table的辅助函数组成。他的主要作用之一是对Lua中array的大小给出一个合理的解释。另外还提供了一些从list中插入删除元素的函数,以及对array元素排序函数。
## table.concat(table, sep, start, end)
concat是concatenate(连锁, 连接)的缩写. table.concat()函数列出参数中指定table的数组部分从start位置到end位置的所有元素, 元素间以指定的分隔符(sep)隔开。除了table外, 其他的参数都不是必须的, 分隔符的默认值是空字符, start的默认值是1, end的默认值是数组部分的总长.
sep, start, end这三个参数是顺序读入的, 所以虽然它们都不是必须参数, 但如果要指定靠后的参数, 必须同时指定前面的参数.
~~~
test = {"Tom", "Mary", "Jam","Hey"}
print(table.concat(test, ":"))
print("*************")
print(table.concat(test, nil, 1, 2))
print("*************")
print(table.concat(test, "\n", 2, 3))
print(table.maxn(test))
~~~
运行结果
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-22_56cb2ca156d59.jpg)
## table.insert(table, pos, value)
table.insert()函数在table的数组部分指定位置(pos)插入值为value的一个元素. pos参数可选, 默认为数组部分末尾.
~~~
network = {
{name = "Tom" ,IP = "210.26.30.34"},
{name = "Mary" ,IP = "210.26.30.23"},
{name = "Jam" ,IP = "210.26.30.12"},
{name = "hey" ,IP = "210.26.30.30"},
}
--添加数据
table.insert(network,{name = "Feng" ,IP = "210.26.30.11"})
~~~
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-22_56cb2ca167c30.jpg)
## table.remove(table, pos)
table.remove()函数删除并返回table数组部分位于pos位置的元素. 其后的元素会被前移. pos参数可选, 默认为table长度, 即从最后一个元素删起.
~~~
network = {
{name = "Tom" ,IP = "210.26.30.34"},
{name = "Mary" ,IP = "210.26.30.23"},
{name = "Jam" ,IP = "210.26.30.12"},
{name = "hey" ,IP = "210.26.30.30"},
}
--删除最后一条数据
table.remove(network,#network)
~~~
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-22_56cb2ca17ddba.jpg)
## table.sort(table, comp)
table.sort(table, comp)
table.sort()函数对给定的table进行升序排序. comp是一个可选的参数, 此参数是一个外部函数, 可以用来自定义sort函数的排序标准.
此函数应满足以下条件: 接受两个参数(依次为a, b), 并返回一个布尔型的值, 当a应该排在b前面时, 返回true, 反之返回false.
~~~
network = {"Tom","Jam","Mary"}
--升序
table.sort(network)
--降序
table.sort(network,function(a,b) return a > b end)
~~~
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-22_56cb2ca18cc19.jpg)
对于table.sort进行排序时,它还接受一个table并对其中的元素排序,如:升序、降序、按数字顺序、按符顺序或table中KEY的顺序。
~~~
network = {
{name = "Tom" ,IP = "210.26.30.34"},
{name = "Mary" ,IP = "210.26.30.23"},
{name = "Jam" ,IP = "210.26.30.12"},
{name = "hey" ,IP = "210.26.30.30"},
}
table.sort(network,function(a,b) return (a.IP < b.IP)end) --升序
~~~
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-22_56cb2ca19d468.jpg)
## table.foreachi(table, function(i, v))
会期望一个从 1(数字 1)开始的连续整数范围,遍历table中的key和value逐对进行function(i, v)操作
~~~
t1 = {2, 4, 6, language="Lua", version="5", 8, 10, 12, web="hello lua"};
table.foreachi(t1, function(i, v) print (i, "->",v) end)--等价于foreachi(t1, print)
~~~
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-22_56cb2ca1adb77.jpg)
## table.foreach(table, function(i, v))
与foreachi不同的是,foreach会对整个表进行迭代。
~~~
t1 = {2, 4, 6, language="Lua", version="5", 8, 10, 12, web="hello lua"};
table.foreach(t1, function(i, v) print (i,"->", v) end) ;
~~~
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-22_56cb2ca1bd9f4.jpg)
## table.maxn(table)
table.maxn()函数返回指定table中所有正数key值中最大的key值. 如果不存在key值为正数的元素, 则返回0. 此函数不限于table的数组部分.
~~~
test = {"Tom", "Mary", "Jam","Hey"}--默认test[1] = "Tom",test[2] = "Mary",test[3] = "Jam",test[4] = "Hey"
print(table.maxn(test))
~~~
4
## table.getn(table)
返回table中元素的个数。注意:该table的key必须是有序的,索引是从1开始的, 若无序,无法得到正确的大小。如:
~~~
t1 = {2, 4, 6, language="Lua", version="5", 8, 10, 12, web="hello lua"};
print(table.getn(t1))
~~~
6
若table无序,可以这样得到大小:
~~~
for k,v in pairs(t1) do
count = count + 1
end
print(count)
~~~
9
lua学习笔记之五(Lua中的数学库)
最后更新于:2022-04-01 10:07:12
### Lua中的数学库
Lua5.1中数学库的所有函数如下表:
math.pi 为圆周率常量 = 3.14159265358979323846
表1
<table border="1" cellspacing="0" cellpadding="0" width="574"><tbody><tr><td style="background:#99CCFF"><p align="center">数学库</p></td><td style="background:#99CCFF"><p align="center">说明</p></td><td style="background:#99CCFF"><p align="center">例子</p></td><td style="background:#99CCFF"><p align="center">方法</p></td></tr><tr><td><p align="center"><span style="color:#89C507">abs</span></p></td><td valign="bottom"><p align="left">取绝对值</p></td><td valign="bottom"><p align="left">math.abs(-15)</p></td><td valign="bottom"><p align="left">15</p></td></tr><tr><td><p align="center"><span style="color:#89C507">acos</span></p></td><td valign="bottom"><p align="left">反余弦函数</p></td><td valign="bottom"><p align="left">math.acos(0.5)</p></td><td valign="bottom"><p align="left">1.04719755</p></td></tr><tr><td><p align="center"><span style="color:#89C507">asin</span></p></td><td valign="bottom"><p align="left">反正弦函数</p></td><td valign="bottom"><p align="left">math.asin(0.5)</p></td><td valign="bottom"><p align="left">0.52359877</p></td></tr><tr><td><p align="center"><span style="color:#89C507">atan2</span></p></td><td valign="bottom"><p align="left">x / y的反正切值</p></td><td valign="bottom"><p align="left">math.atan2(90.0, 45.0)</p></td><td valign="bottom"><p align="left">1.10714871</p></td></tr><tr><td><p align="center"><span style="color:#89C507">atan</span></p></td><td valign="bottom"><p align="left">反正切函数</p></td><td valign="bottom"><p align="left">math.atan(0.5)</p></td><td valign="bottom"><p align="left">0.463647609</p></td></tr><tr><td><p align="center"><span style="color:#89C507">ceil</span></p></td><td valign="bottom"><p align="left">不小于x的最大整数</p></td><td valign="bottom"><p align="left">math.ceil(5.8)</p></td><td valign="bottom"><p align="left">6</p></td></tr><tr><td><p align="center"><span style="color:#89C507">cosh</span></p></td><td valign="bottom"><p align="left">双曲线余弦函数</p></td><td valign="bottom"><p align="left">math.cosh(0.5)</p></td><td valign="bottom"><p align="left">1.276259652</p></td></tr><tr><td><p align="center"><span style="color:#89C507">cos</span></p></td><td valign="bottom"><p align="left">余弦函数</p></td><td valign="bottom"><p align="left">math.cos(0.5)</p></td><td valign="bottom"><p align="left">0.87758256</p></td></tr><tr><td><p align="center"><span style="color:#89C507">deg</span></p></td><td valign="bottom"><p align="left">弧度转角度</p></td><td valign="bottom"><p align="left">math.deg(math.pi)</p></td><td valign="bottom"><p align="left">180</p></td></tr><tr><td><p align="center"><span style="color:#89C507">exp</span></p></td><td valign="bottom"><p align="left">计算以e为底x次方值</p></td><td valign="bottom"><p align="left">math.exp(2)</p></td><td valign="bottom"><p align="left">2.718281828</p></td></tr><tr><td><p align="center"><span style="color:#89C507">floor</span></p></td><td valign="bottom"><p align="left">不大于x的最大整数</p></td><td valign="bottom"><p align="left">math.floor(5.6)</p></td><td valign="bottom"><p align="left">5</p></td></tr><tr><td><p align="center"><span style="color:#89C507">fmod </span><span style="color:#89C507">(</span><span style="color:#89C507">mod</span><span style="color:#89C507">)</span></p></td><td valign="bottom"><p align="left">取模运算</p></td><td valign="bottom"><p align="left">math.mod(14, 5)</p></td><td valign="bottom"><p align="left">4</p></td></tr><tr><td><p align="center"><span style="color:#89C507">frexp</span></p></td><td valign="bottom"><p align="left">把双精度数val分解为数字部分(尾数)和以2为底的指数n,即val=x*2n</p></td><td valign="bottom"><p align="left">math.frexp(10.0)</p></td><td valign="bottom"><p align="left">0.625 4</p></td></tr><tr><td><p align="center"><span style="color:#89C507">ldexp</span></p></td><td valign="bottom"><p align="left">计算value * 2的n次方</p></td><td valign="bottom"><p align="left">math.ldexp(10.0, 3)</p></td><td valign="bottom"><p align="left">80 = 10 * (2 ^3)</p></td></tr><tr><td><p align="center"><span style="color:#89C507">log10</span></p></td><td valign="bottom"><p align="left">计算以10为基数的对数</p></td><td valign="bottom"><p align="left">math.log10(100)</p><p align="left"> </p></td><td valign="bottom"><p align="left">2</p></td></tr><tr><td><p align="center"><span style="color:#89C507">log</span></p></td><td valign="bottom"><p align="left">计算一个数字的自然对数</p></td><td valign="bottom"><p align="left">math.log(2.71)</p></td><td valign="bottom"><p align="left">0.9969</p></td></tr><tr><td><p align="center"><span style="color:#89C507">max</span></p></td><td valign="bottom"><p align="left">取得参数中最大值</p></td><td valign="bottom"><p align="left">math.max(2.71, 100, -98, 23)</p></td><td valign="bottom"><p align="left">100</p></td></tr><tr><td><p align="center"><span style="color:#89C507">min</span></p></td><td valign="bottom"><p align="left">取得参数中最小值</p></td><td valign="bottom"><p align="left">math.min(2.71, 100, -98, 23)</p></td><td valign="bottom"><p align="left">-98</p></td></tr><tr><td><p align="center"><span style="color:#89C507">modf</span></p></td><td valign="bottom"><p align="left">把数分为整数和小数</p></td><td valign="bottom"><p align="left">math.modf(15.98)</p></td><td valign="bottom"><p align="left">15 98</p></td></tr><tr><td><p align="center"><span style="color:#89C507">pow</span></p></td><td valign="bottom"><p align="left">得到x的y次方</p></td><td valign="bottom"><p align="left">math.pow(2, 5)</p></td><td valign="bottom"><p align="left">32</p></td></tr><tr><td><p align="center"><span style="color:#89C507">rad</span></p></td><td valign="bottom"><p align="left">角度转弧度</p></td><td valign="bottom"><p align="left">math.rad(180)</p></td><td valign="bottom"><p align="left">3.14159265358</p></td></tr><tr><td><p align="center"><span style="color:#89C507">random</span></p></td><td valign="bottom"><p align="left">获取随机数</p></td><td valign="bottom"><p align="left">math.random(1, 100)
math.random(100)</p></td><td valign="bottom"><p align="left">获取1-100的随机数</p></td></tr><tr><td><p align="center"><span style="color:#89C507">randomseed</span></p></td><td valign="bottom"><p align="left">设置随机数种子</p></td><td valign="bottom"><p align="left">math.randomseed(os.time())</p></td><td valign="bottom"><p align="left">在使用math.random函数之前必须使用此函数设置随机数种子</p></td></tr><tr><td><p align="center"><span style="color:#89C507">sinh</span></p></td><td valign="bottom"><p align="left">双曲线正弦函数</p></td><td valign="bottom"><p align="left">math.sinh(0.5)</p></td><td valign="bottom"><p align="left">0.5210953</p></td></tr><tr><td><p align="center"><span style="color:#89C507">sin</span></p></td><td valign="bottom"><p align="left">正弦函数</p></td><td valign="bottom"><p align="left">math.sin(math.rad(30))</p></td><td valign="bottom"><p align="left">0.5</p></td></tr><tr><td><p align="center"><span style="color:#89C507">sqrt</span></p></td><td valign="bottom"><p align="left">开平方函数</p></td><td valign="bottom"><p align="left">math.sqrt(16)</p></td><td valign="bottom"><p align="left">4</p></td></tr><tr><td><p align="center"><span style="color:#89C507">tanh</span></p></td><td valign="bottom"><p align="left">双曲线正切函数</p></td><td valign="bottom"><p align="left">math.tanh(0.5)</p></td><td valign="bottom"><p align="left">0.46211715</p></td></tr><tr><td><p align="center"><span style="color:#89C507">tan</span></p></td><td valign="bottom"><p align="left">正切函数</p></td><td valign="bottom"><p align="left">math.tan(0.5)</p></td><td valign="bottom"><p align="left">0.5463024</p></td></tr></tbody></table>
引用博客:http://www.cnblogs.com/whiteyun/archive/2009/08/10/1543040.html
lua学习笔记之四(Lua中的基本函数库)
最后更新于:2022-04-01 10:07:09
### Lua中的基本函数库
表1
<table border="1" cellspacing="0" cellpadding="0"><tbody><tr><td valign="top" style="background:#99CCFF"><p style="text-align:center">基本函数库</p></td><td valign="top" style="background:#99CCFF"><p style="text-align:center">功能</p></td><td valign="top" style="background:#99CCFF"><p style="text-align:center">参数</p></td><td valign="top" style="background:#99CCFF"><p>备注</p></td></tr><tr><td valign="top"><p><span style="color:rgb(137,197,7)">assert(v[,mess age]) </span></p></td><td valign="top"><p>相当于C的断言</p></td><td valign="top"><p align="left">v:当表达式v为nil或false将触发错误,</p><p align="left">message:发生错误时返回的信息,默认为"assertion failed!"</p></td><td valign="top"><p> </p></td></tr><tr><td valign="top"><p><span style="color:#89C507">collectgarbage (opt [, arg])</span></p></td><td valign="top"><p>是垃圾收集器的通用接口,用于操作垃圾收集器</p></td><td valign="top"><p align="left">opt:操作方法标志</p><p align="left">"Stop": 停止垃圾收集器 </p><p align="left">"Restart": 重启垃圾收集器 </p><p align="left">"Collect": 执行一次全垃圾收集循环</p><p align="left">"Count": 返回当前Lua中使用的内存量(以KB为单位)</p><p align="left">"Step": 单步执行一个垃圾收集. 步长 "Size" 由参数arg指定 (大型的值需要多步才能完成),如果要准确指定步长,需要多次实验以达最优效果。如果步长完成一次收集循环,将返回True</p><p align="left">"Setpause": 设置 arg/100 的值作为暂定收集的时长 </p><p align="left">"Setstepmul": 设置 arg/100 的值,作为步长的增幅(即新步长=旧步长*arg/100)</p></td><td valign="top"><p> </p></td></tr><tr><td valign="top"><p><span style="color:#89C507">dofile (filename)</span></p></td><td valign="top"><p>打开并且执行一个lua块,当忽略参数filename时,将执行标准输入设备(stdin)的内容。返回所有块的返回值。当发生错误时,dofile将错误反射给调用者</p></td><td valign="top"><p><span style="color:#FB0007">注:</span><span style="color:#FB0007">dofile</span><span style="color:#FB0007">不能在保护模式下运行</span></p></td><td valign="top"><p> </p></td></tr><tr><td valign="top"><p><span style="color:#89C507">error (message [, level])</span></p></td><td valign="top"><p>终止正在执行的函数,并返回message的内容作为错误信息(error函数永远都不会返回)</p></td><td valign="top"><p align="left">通常情况下,error会附加一些错误位置的信息到message头部.</p><p align="left">Level参数指示获得错误的位置,</p><p align="left">Level=1[默认]:为调用error位置(文件+行号)</p><p align="left">Level=2:指出哪个调用error的函数的函数</p><p>Level=0:不添加错误位置信息</p></td><td valign="top"><p> </p></td></tr><tr><td valign="top"><p><span style="color:#89C507">_G</span><span style="color:#89C507">全局环境表</span><span style="color:#89C507">(</span><span style="color:#89C507">全局变量</span><span style="color:#89C507">)</span></p></td><td valign="top"><p>记录全局环境的变量值的表 _G._G = _G</p></td><td valign="top"><p> </p></td><td valign="top"><p> </p></td></tr><tr><td valign="top"><p><span style="color:#89C507">getfenv(f)</span></p></td><td valign="top"><p>返回函数f的当前环境表</p></td><td valign="top"><p>f可以为函数或调用栈的级别,级别1[默认]为当前的函数,级别0或其它值将返回全局环境_G</p></td><td valign="top"><p> </p></td></tr><tr><td valign="top"><p><span style="color:#89C507">getmetatable(object)</span></p></td><td valign="top"><p>返回指定对象的元表(若object的元表.__metatable项有值,则返回object的元表.__metatable的值),当object没有元表时将返回nil</p></td><td valign="top"><p> </p></td><td valign="top"><p> </p></td></tr><tr><td valign="top"><p align="left"><span style="color:#89C507">ipairs (t)</span></p><p> </p></td><td valign="top"><p align="left">返回三个值 迭代函数、表、0</p><p align="left">多用于穷举表的键名和键值对</p><p align="left">如:for i,v in ipairs(t) do end</p><p align="left">每次循环将索引赋级i,键值赋给v</p></td><td valign="top"><p><span style="color:#FB0007">注:本函数只能用于以数字索引访问的表</span><span style="color:#FB0007">如:</span><span style="color:#FB0007">t={"1","cash"}</span></p></td><td valign="top"><p> </p></td></tr><tr><td valign="top"><p><span style="color:#89C507">load (func [, chunkname])</span></p></td><td valign="top"><p align="left">装载一个块中的函数,每次调用func将返回一个连接前一结的字串,在块结尾处将返回nil</p><p align="left">当没有发生错误时,将返回一个编译完成的块作为函数,否则返回nil加上错误信息,此函数的环境为全局环境</p><p>chunkname用于错误和调试信息</p></td><td valign="top"><p> </p></td><td valign="top"><p> </p></td></tr><tr><td valign="top"><p><span style="color:#89C507">loadfile ([filename])</span></p></td><td valign="top"><p>与load类似,但装载的是文件或当没有指定filename时装载标准输入(stdin)的内容</p></td><td valign="top"><p> </p></td><td valign="top"><p> </p></td></tr><tr><td valign="top"><p><span style="color:#89C507">loadstring (string [, chunkname])</span></p></td><td valign="top"><p align="left">与load类似,但装载的内容是一个字串</p><p>如:assert(loadstring(s))()</p></td><td valign="top"><p> </p></td><td valign="top"><p> </p></td></tr><tr><td valign="top"><p><span style="color:#89C507">next (table [, index])</span></p></td><td valign="top"><p>允许程序遍历表中的每一个字段,返回下一索引和该索引的值。</p></td><td valign="top"><p align="left">table:要遍历的表</p><p align="left">index:要返回的索引的前一索中的号,当index为nil[]时,将返回第一个索引的值,当索引号为最后一个索引或表为空时将返回nil</p><p><span style="color:#FB0007">注:可以用</span><span style="color:#FB0007">next(t)</span><span style="color:#FB0007">来检测表是否为空</span><span style="color:#FB0007">(</span><span style="color:#FB0007">此函数只能用于以数字索引的表与</span><span style="color:#FB0007">ipairs</span><span style="color:#FB0007">相类似</span><span style="color:#FB0007">)</span></p></td><td valign="top"><p> </p></td></tr><tr><td valign="top"><p><span style="color:#89C507">ipairs (t)</span></p></td><td valign="top"><p align="left">返回三个值 next函数、表、0</p><p align="left">多用于穷举表的键名和键值对</p><p>如:for n,v in pairs(t) do end</p></td><td valign="top"><p align="left">每次循环将索引赋级i,键值赋给v</p><p><span style="color:#FB0007">注:本函数只能用于以键名索引访问的表</span><span style="color:#FB0007">如:</span><span style="color:#FB0007">t={id="1",name="cash"}</span></p></td><td valign="top"><p> </p></td></tr><tr><td valign="top"><p><span style="color:#89C507">pcall (f, arg1, ···)</span></p></td><td valign="top"><p align="left">在保护模式下调用函数(即发生的错误将不会反射给调用者)</p><p>当调用函数成功能返回true,失败时将返回false加错误信息</p></td><td valign="top"><p> </p></td><td valign="top"><p> </p></td></tr><tr><td valign="top"><p><span style="color:#89C507">print (···)</span></p></td><td valign="top"><p>简单的以tostring方式格式化输出参数的内容</p></td><td valign="top"><p> </p></td><td valign="top"><p> </p></td></tr><tr><td valign="top"><p><span style="color:#89C507">rawequal (v1, v2)</span></p></td><td valign="top"><p>检测v1是否等于v2,此函数不会调用任何元表的方法</p></td><td valign="top"><p> </p></td><td valign="top"><p> </p></td></tr><tr><td valign="top"><p><span style="color:#89C507">rawget (table, index)</span></p></td><td valign="top"><p align="left">获取表中指定索引的值,此函数不会调用任何元表的方法,成功返回相应的值,当索引不存在时返回nil</p></td><td valign="top"><p><span style="color:#FB0007">注:本函数只能用于以数字索引访问的表</span><span style="color:#FB0007">如:</span><span style="color:#FB0007">t={"1","cash"}</span></p></td><td valign="top"><p> </p></td></tr><tr><td valign="top"><p><span style="color:#89C507">rawset (table, index, value)</span></p></td><td valign="top"><p>设置表中指定索引的值,此函数不会调用任何元表的方法,此函数将返回table</p></td><td valign="top"><p> </p></td><td valign="top"><p> </p></td></tr><tr><td valign="top"><p><span style="color:#89C507">select (index, ···)</span></p></td><td valign="top"><p align="left">当index为数字将返回所有index大于index的参数:如:select(2,"a","b") 返回 "b"</p><p align="left">当index为"#",则返回参数的总个数(不包括index)</p></td><td valign="top"><p> </p></td><td valign="top"><p> </p></td></tr><tr><td valign="top"><p><span style="color:#89C507">setfenv (f, table)</span></p></td><td valign="top"><p>设置函数f的环境表为table</p></td><td valign="top"><p>f可以为函数或调用栈的级别,级别1[默认]为当前的函数,级别0将设置当前线程的环境表</p></td><td valign="top"><p> </p></td></tr><tr><td valign="top"><p><span style="color:#89C507">setmetatable (table, metatable)</span></p></td><td valign="top"><p align="left">指定的table设置元表metatable,如果metatable为nil则取消table的元表,当metatable有__metatable字段时,将触发错误</p></td><td valign="top"><p><span style="color:#FB0007">注:只能为</span><span style="color:#FB0007">LUA_TTABLE</span><span style="color:#FB0007">表类型指定元表</span></p></td><td valign="top"><p> </p></td></tr><tr><td valign="top"><p><span style="color:#89C507">tonumber (e [, base])</span></p></td><td valign="top"><p>尝试将参数e转换为数字,当不能转换时返回nil</p></td><td valign="top"><p>base(2~36)指出参数e当前使用的进制,默认为10进制,如tonumber(11,2)=3</p></td><td valign="top"><p> </p></td></tr><tr><td valign="top"><p><span style="color:#89C507">tostirng(e)</span></p></td><td valign="top"><p align="left">将参数e转换为字符串,此函数将会触发元表的__tostring事件</p></td><td valign="top"><p> </p></td><td valign="top"><p> </p></td></tr><tr><td valign="top"><p><span style="color:#89C507">type(v)</span></p></td><td valign="top"><p>返回参数的类型名("nil","number", "string", "boolean", "table", "function", "thread", "userdata")</p></td><td valign="top"><p> </p></td><td valign="top"><p> </p></td></tr><tr><td valign="top"><p><span style="color:#89C507">unpack (list [, i [, j]])</span></p></td><td valign="top"><p align="left">返回指定表的索引的值,i为起始索引,j为结束索引</p></td><td valign="top"><p><span style="color:#FB0007">注:本函数只能用于以数字索引访问的表</span><span style="color:#FB0007">,</span><span style="color:#FB0007">否则只会返回</span><span style="color:#FB0007">nil</span><span style="color:#FB0007">如:</span><span style="color:#FB0007">t={"1","cash"}</span></p></td><td valign="top"><p> </p></td></tr><tr><td valign="top"><p><span style="color:#89C507">_VERSION</span></p></td><td valign="top"><p>返回当前Lua的版本号"Lua 5.1".</p></td><td valign="top"><p> </p></td><td valign="top"><p> </p></td></tr><tr><td valign="top"><p><span style="color:#89C507">xpcall (f, err)</span></p></td><td valign="top"><p align="left">与pcall类似,在保护模式下调用函数(即发生的错误将不会反射给调用者)</p><p align="left">但可指定一个新的错误处理函数句柄</p><p>当调用函数成功能返回true,失败时将返回false加err返回的结果</p></td><td valign="top"><p> </p></td><td valign="top"><p> </p></td></tr></tbody></table>
引用博客:http://www.cnblogs.com/whiteyun/archive/2009/08/12/1543184.html
lua学习笔记之三(C/C++程序员的Lua快速入门[高阶话题])
最后更新于:2022-04-01 10:07:07
## 高阶话题
## 1.迭代
**1.1 实例代码:**
~~~
--迭代
local function enum(array)
local index = 1
return function()
local ret = array[index]
index = index + 1
return ret
end
end
local function foreach(array,action)
for element in enum(array)do
action(element)
end
end
foreach({1,2,3},print)
~~~
**输出结果:**
1
2
3
**1.2 有关迭代的描述:**
- 定义
迭代是for语句的一种特殊形式,可以通过for语句驱动迭代函数对一个给定集合进行遍历。正式、完备的语法说明较复杂,请参考Lua手册。
- 实现
如前面代码所示:enum函数返回一个匿名的迭代函数,for语句每次调用该迭代函数都得到一个值(通过element变量引用),若该值为nil,则for循环结束。
## 2.协作线程
**2.1 实例代码**
~~~
--线程
local function producer()
return coroutine.create(
function(salt)
local t = {1,2,3}
for i = 1,#t do
salt = coroutine.yield(t[i] + salt)
end
end
)
end
function consumer(prod)
local salt = 10
while true do
local running ,product = coroutine.resume(prod, salt)
salt = salt*salt
if running then
print(product or "END!")
else
break
end
end
end
consumer(producer())
~~~
**输出结果:**
11
102
10003
END!
**2.2 有关协作线程的描述:**
- 创建协作线程
通过coroutine.create可以创建一个协作线程,该函数接收一个函数类型的参数作为线程的执行体,返回一个线程对象。
- 启动线程
通过coroutine.resume可以启动一个线程或者继续一个挂起的线程。该函数接收一个线程对象以及其他需要传递给该线程的参数。线程可以通过线程函数的参数或者coroutine.yield调用的返回值来获取这些参数。当线程初次执行时,resume传递的参数通过线程函数的参数传递给线程,线程从线程函数开始执行;当线程由挂起转为执行时,resume传递的参数以yield调用返回值的形式传递给线程,线程从yield调用后继续执行
- 线程放弃调度
线程调用coroutine.yield暂停自己的执行,并把执行权返回给启动/继续它的线程;线程还可利用yield返回一些值给后者,这些值以resume调用的返回值的形式返回。
## 附录 常用的Lua参考资料
[lua 论坛](http://www.luaer.cn)(lua 中国开发者 luaer中国官司方网站)
[Lua参考手册](http://www.lua.org/manual/5.1/)(最正式、权威的Lua文档)
[Lua编程](http://www.lua.org/pil/)(在线版,同样具权威性的Lua教科书)
[Lua正式网站的文档页面](http://www.lua.org/docs.html)(包含很多有价值的文档资料链接)
[Lua维基](http://lua-users.org/wiki/)(最全面的Lua维基百科)
[LuaForge](http://luaforge.net/projects/)(最丰富的Lua开源代码基地)
参考文献《C/C++程序员的Lua快速入门》
lua学习笔记之二(C/C++程序员的Lua快速入门[进阶话题])
最后更新于:2022-04-01 10:07:05
## 进阶话题
## 1.函数闭包
**1.1 实例代码**
~~~
function createCountdownTimer(second)
local ms = second * 1000 --ms为countDown的Upvalue
local function countDown()
ms = ms -1
return ms
end
return countDown
end
local timer1 = createCountdownTimer(1)
for i = 1, 3 do
print(timer1())
end
~~~
输出结果:
**999**
**998**
**997**
**1.2 关于函数闭包描述**
- Upvalue
一个函数所使用的定义在它的函数体之外的局部变量(external local variable)称为这个函数的upvalue。 在前面的代码中,函数countDown使用的定义在函数createCountdownTimer 中的局部变量ms就是countDown的upvalue,但ms对createCountdownTimer而 言只是一个局部变量,不是upvalue。 Upvalue是Lua不同于C/C++的特有属性,需要结合代码仔细体会。
- 函数闭包
一个函数和它所使用的所有upvalue构成了一个函数闭包。
- Lua函数闭包与C函数的比较
Lua函数闭包使函数具有保持它自己的状态的能力,从这个意义上说,可以 与带静态局部变量的C函数相类比。但二者有显著的不同:对Lua来说,函数 是一种基本数据类型——代表一种(可执行)对象,可以有自己的状态;但 是对带静态局部变量的C函数来说,它并不是C的一种数据类型,更不会产生 什么对象实例,它只是一个静态地址的符号名称。
## 2. 基于对象的实现方式
**2.2 实例代码**
~~~
local function create(name ,id )
local data = {name = name ,id = id} --data为obj.SetName,obj.GetName,obj.SetId,obj.GetId的Upvalue
local obj = {} --把需要隐藏的成员放在一张表里,把该表作为成员函数的upvalue。
function obj.SetName(name)
data.name = name
end
function obj.GetName()
return data.name
end
function obj.SetId(id)
data.id = id
end
function obj.GetId()
return data.id
end
return obj
end
~~~
输出结果:
mycreate's id:1mycreate's name:Sam
mycreate's id:1mycreate's name:Lucy
**2.2 有关对象实现的描述**
**实现方式**: 把需要隐藏的成员放在一张表里,把该表作为成员函数的upvalue。
**局限性**: 基于对象的实现不涉及继承及多态。但另一方面,脚本编程是否需要继承和多态要视情况而定。
## 3.元表
**3.1 实例代码(1):**
~~~
local t = {}
local m = {a = "and",b = "Li Lei", c = "Han Meimei"}
setmetatable(t,{__index = m}) --表{ __index=m }作为表t的元表
for k,v in pairs(t) do --穷举表t
print("有值吗?")
print(k,"=>",v)
end
print("-------------")
print(t.b, t.a, t.c)
~~~
输出结果:
-------------
Li LeiandHan Meimei
**3.2 实例代码(2):**
~~~
local function add(t1,t2)
--‘#’运算符取表长度
assert(#t1 == #t2)
local length = #t1
for i = 1,length do
t1[i] = t1[i] + t2[i]
end
return t1
end
--setmetatable返回被设置的表
t1 = setmetatable({1,2,3},{__add = add})
t2 = setmetatable({10,20,30},{__add = add})
for k,v in pairs(t1) do
print(k,"=>",v)
end
for k,v in pairs(t2) do
print(k,"=>",v)
end
print("---------两元表相加--------------")
t1 = t1 + t2
for i = 1 ,#t1 do
print(t1[i])
end
~~~
输出结果:
1=>1
2=>2
3=>3
1=>10
2=>20
3=>30
---------两元表相加--------------
11
22
33
**3.3 有关元表的描述:**
**定义 :**
元表本身只是一个普通的表,通过特定的方法(比如setmetatable)设置到某个对象上,进而影响这个对象的行为;一个对象有哪些行为受到元表影响以及这些行为按照何种方式受到影响是受Lua语言约束的。比如在前面的代码里,两个表对象的加法运算,如果没有元表的干预,就是一种错误;但是Lua规定了元表可以“重载”对象的加法运算符,因此若把定义了加法运算的元表设置到那两个表上,它们就可以做加法了。元表是Lua最关键的概念之一,内容也很丰富,请参考Lua文档了解详情。
**元表与C++虚表的比较:**
如果把表比作对象,元表就是可以改变对象行为的“元”对象。在某种程度上,元表可以与C++的虚表做一类比。但二者还是迥然不同的:元表可以动态的改变,C++虚表是静态不变的;元表可以影响表(以及其他类型的对象)的很多方面的行为,虚表主要是为了定位对象的虚方法(最多再带上一点点RTTI)。
## 4. 基于原型的继承
4.1 实例代码
~~~
local Robot = { name = "Sam", id = 001 }
function Robot:New(extension)
local t = setmetatable(extension or { }, self)
self.__index = self
return t
end
function Robot:SetName(name)
self.name = name
end
function Robot:GetName()
return self.name
end
function Robot:SetId(id)
self.id = id end
function Robot:GetId()
return self.id
end
robot = Robot:New()
print("robot's name:", robot:GetName())
print("robot's id:", robot:GetId())
print("-----------------")
local FootballRobot = Robot:New({position = "right back"})
function FootballRobot:SetPosition(p)
self.position = p
end
function FootballRobot:GetPosition()
return self.position
end
fr = FootballRobot:New()
print("fr's position:", fr:GetPosition())
print("fr's name:", fr:GetName())
print("fr's id:", fr:GetId())
print("-----------------")
fr:SetName("Bob")
print("fr's name:", fr:GetName())
print("robot's name:", robot:GetName())
~~~
输出结果:
robot's name:Sam
robot's id:1
-----------------
fr's position:right back
fr's name:Sam
fr's id:1
-----------------
fr's name:Bob
robot's name:Sam
**4.2 相关描述:**
prototype模式一个对象既是一个普通的对象,同时也可以作为创建其他对象的原型的对象(即类对象,class object);动态的改变原型对象的属性就可以动态的影响所有基于此原型的对象;另外,基于一个原型被创建出来的对象可以重载任何属于这个原型对象的方法、属性而不影响原型对象;同时,基于原型被创建出来的对象还可以作为原型来创建其他对象。
## 5.包
**5.1 实例代码:**
hello.lua
~~~
local pack = require "mypack" --导入包[注:包的名字与定义包的文件的名字相同(除去文件名后缀,在前面的代码中,就是“mypack”)]
print(ver or "No ver defined!")
print(pack.ver)
pack.aFunInMyPack()
print(aFunInMyPack or "No aFunInMyPack defined!")
aFuncFromMyPack()
~~~
mypack.lua
~~~
module(..., package.seeall) --定义包
ver = "0.1 alpha"
function aFunInMyPack()
print("Hello!")
end
_G.aFuncFromMyPack = aFunInMyPack
~~~
输出结果:
No ver defined!
0.1 alpha
Hello!
No aFunInMyPack defined!
Hello!
**
**
**5.2有关包的描述:**
- 定义
包是一种组织代码的方式。
- 实现方式
一般在一个Lua文件内以module函数开始定义一个包。module同时定义了一个新的包的函数环境,以使在此包中定义的全局变量都在这个环境中,而非使用包的函数的环境中。理解这一点非常关键。以前面的代码为例, “module(..., package.seeall)”的意思是定义一个包,包的名字与定义包的文件的名字相同(除去文件名后缀,在前面的代码中,就是“mypack”),并且在包的函数环境里可以访问使用包的函数环境(比如,包的实现使用了print,这个变量没有在包里定义,而是定义在使用包的外部环境中)。
- 使用方式
一般用require函数来导入一个包,要导入的包必须被置于包路径(packagepath)上。包路径可以通过package.path或者环境变量来设定。一般来说,当前工作路径总是在包路径中。
- 其他
请参考Lua手册进一步了解包的详细说明。
参考文献《C/C++程序员的Lua快速入门》
lua学习笔记之一(C/C++程序员的Lua快速入门[初阶话题])
最后更新于:2022-04-01 10:07:03
## 前言
本文针对的读者是有经验的C/C++程序员,希望了解Lua或者迅速抓住Lua的关键概念和模式进行开发的。因此本文并不打算教给读者条件语句的语法或者函数定义的方式等等显而易见的东西,以及一些诸如变量、函数等编程语言的基本概念。本文只打算告诉读者Lua那些与C/C++显著不同的东西以及它们实际上带来了怎样不同于C/C++的思考方式。不要小看它们,它们即将颠覆你传统的C/C++的世界观!
本文一共分[初阶](http://blog.csdn.net/rexuefengye/article/details/13289777)、[进阶](http://blog.csdn.net/rexuefengye/article/details/13290603)和[高阶](http://blog.csdn.net/rexuefengye/article/details/13293099)三大部分,每个部分又有若干章节。读者应当从头至尾循序渐进的阅读,但是标有“*”号的章节(主要讨论OO在Lua中的实现方式)可以略去而不影响对后面内容的理解。读者只要把前两部分完成就可以胜任Lua开发的绝大部分任务。高阶部分可作为选择。
## 初阶话题
### 1.八种基本类型: 如下表
<table border="1" cellspacing="0" cellpadding="0"><tbody><tr><td valign="top" style="background:#99CCFF"><p align="center">基本类型</p></td><td valign="top" style="background:#99CCFF"><p align="center">描述</p></td><td valign="top" style="background:#99CCFF"><p align="center">备注</p></td></tr><tr><td valign="top"><p align="center">数值(number)</p></td><td valign="top"><p>内部以double表示</p></td><td valign="top"><p> </p></td></tr><tr><td valign="top"><p align="center">字符串(string)</p><p align="center"> </p></td><td valign="top"><p>总是以零结尾,但可以包含任意字符(包括零),因此并不等价于C字符串, 而是其超集</p></td><td valign="top"><p> </p></td></tr><tr><td valign="top"><p align="center">布尔(boolean)</p></td><td valign="top"><p>只有“true”或者“false”两个值。</p></td><td valign="top"><p> </p></td></tr><tr><td valign="top"><p align="center">函数(function)</p><p align="center"> </p></td><td valign="top"><p>Lua的关键概念之一。不简单等同于C的函数或函数指针。</p></td><td valign="top"><p> </p></td></tr><tr><td valign="top"><p align="center">表(table)</p></td><td valign="top"><p>异构的Hash表。Lua的关键概念之一。</p></td><td valign="top"><p> </p></td></tr><tr><td valign="top"><p align="center">userdata</p><p align="center"> </p></td><td valign="top"><p>用户(非脚本用户)定义的C数据结构。脚本用户只能使用它,不能定义。</p></td><td valign="top"><p> </p></td></tr><tr><td valign="top"><p align="center">线程(thread)</p><p align="center"> </p></td><td valign="top"><p>Lua协作线程(coroutine),与一般操作系统的抢占式线程不一样。</p></td><td valign="top"><p> </p></td></tr><tr><td valign="top"><p align="center">nil</p><p align="center"> </p></td><td valign="top"><p>代表什么也没有,可以与C的NULL作类比,但它不是空指针。</p></td><td valign="top"><p> </p></td></tr></tbody></table>
### 2.函数
2.1 实例代码
~~~
function foo(a,b,c,...)
local sum = a+b
return sum,c --函数可以返回多个值
end
r1,r2 = foo(1,"123","hello")--平行赋值
print(r1,r2);
~~~
输出结果:
124 hello
2.2 函数基本使用方法
- 函数定义:
用关键字function定义函数,以关键字end结束
- 局部变量:
用关键字local定义。如果没有用local定义,即使在函数内部定义的变量也是全局变量!
- 函数可以返回多个值:
return a, b, c, ...
- 平行赋值:
a, b = c, d
- 全局变量:
前面的代码定义了三个全局变量:foo、r1和r2
### 3.表
3.1 实现代码
~~~
local a = {}
local b = {x = 1,["hello,"] = "world!"}
a["astring"] = "ni,hao!"
a[1] = 100
a["a table"] = b
for k,v in pairs(a) do
print(k,"=>",v);
end
~~~
输出结果:
**1=>100**
**astring=>ni,hao!**
**a table=>table: 0xfd59570**
3.2 表使用方法
- 定义表(Table)的方式
a = {}, b = {...}
- 访问表的成员
通过“.”或者“[]”运算符来访问表的成员。
注意:表达式a.b等价于a[“b”],但不等价于a[b]
- 表项的键和值
任何类型的变量,除了nil,都可以做为表项的键。从简单的数值、字符串到复杂的函数、表等等都可以;同样,任何类型的变量,除了nil,都可以作为表项的值。给一个表项的值赋nil意味着从表中删除这一项,比如令a.b= nil,则把表a中键为“b”的项删除。如果访问一个不存在的表项,其值也是nil,比如有c = a.b,但表a中没有键为“b”的项,则c等于nil。
### 4.一种简单的对象实现方式
4.1 实现代码
~~~
function create(name,id)
local obj = {name = name,id = id}
function obj:SetName(name)
self.name = name
end
function obj:GetName()
return self.name
end
function obj:SetId(id)
self.id = id
end
function obj:GetId()
return self.id
end
return obj
end
local myCreate = create("sam",001)
for k,v in pairs(myCreate) do
print(k,"=>",v)
end
print("myCreate's name:",myCreate:GetName(),"myCreate's id:",myCreate.GetId(myCreate))
myCreate:SetId(100)
myCreate:SetName("Hello Kity")
print("myCreate's name:",myCreate:GetName(),"myCreate's id:",myCreate:GetId())
~~~
**SetName=>function: 0x85efc50**
**GetId=>function: 0x85efc10**
**id=>1**
**SetId=>function: 0x85efd00**
**GetName=>function: 0x85efce0**
**name=>sam**
**myCreate's name:sammyCreate's id:1**
**myCreate's name:Hello KitymyCreate's id:100**
4.2对象实现描述
- 对象工厂模式
如前面代码的create函数
- 用表来表示对象
把对象的数据和方法都放在一张表内,虽然没有隐藏私有成员,但对于简单脚本来说完全可以接受。
- 成员方法的定义
function obj:method(a1, a2, ...) ... end 等价于function obj.method(self, a1, a2, ...) ... end 等价于obj.method = function (self, a1, a2, ...) ... end
- 成员方法的调用
obj:method(a1, a2, ...) 等价于obj.method(obj, a1, a2, ...)
### 5.简单继承
5.1 实现代码
~~~
local function CreateRobot(name,id)
local obj = {name = name,id = id}
function obj:SetName(name)
self.name = name
end
function obj:GetName()
return self.name
end
function obj:SetId(id)
self.id = id
end
function obj:GetId()
return self.id
end
return obj
end
local function createFootballRobot(name ,id ,position)
local obj = CreateRobot(name ,id)
obj.position = "right back"
function obj:SetPosition(p)
self.position = p
end
function obj:GetPosition()
return self.position
end
return obj
end
local mycreateFootballRobot = createFootballRobot("Tom",1000,"广州")
print("mycreateFootballRobot's name:",mycreateFootballRobot:GetName(),"myCreate's id:",mycreateFootballRobot:GetId(),mycreateFootballRobot:GetPosition())
mycreateFootballRobot:SetName("麦迪")
mycreateFootballRobot:SetId(2000)
mycreateFootballRobot:SetPosition("北京")
print("mycreateFootballRobot's name:",mycreateFootballRobot:GetName(),"myCreate's id:",mycreateFootballRobot:GetId(),mycreateFootballRobot:GetPosition())
~~~
输出结果:
mycreateFootballRobot's name:TommyCreate's id:1000right back
mycreateFootballRobot's name:麦迪myCreate's id:2000北京
5.2 简单继承优缺点
优点: 简单、直观
缺点: 传统、不够动态
参考文献《C/C++程序员的Lua快速入门》
前言
最后更新于:2022-04-01 10:07:00
> 原文出处:[Lua学习笔记](http://blog.csdn.net/column/details/luanote.html)
作者:[rexuefengye](http://blog.csdn.net/rexuefengye)
**本系列文章经作者授权在看云整理发布,未经作者允许,请勿转载!**
# Lua学习笔记
> Lua是一种动态类型的语言。在语言中没有类型定义的语法,每个值都带有其自身的类型信息。本系列教程为您介绍Lua语言的基础知识。快速了解Lua语言,掌握Lua语言基础。