Lua学习

1.lua元表和元方法:

  __add(a, b) --加法
  __index(a, b) --索引查询
  __newindex(a, b, c) --索引更新(PS:不懂的话,后面会有讲)
  __call(a, ...) --执行方法调用
  __tostring(a) --字符串输出
  __metatable --保护元表

  _index元方法:
    是否还记得当我们访问一个table中不存在的字段时,会返回什么值?默认情况下,当我们访问一个table中不存在的字段时,得到的结果是nil。但是这种状况很容易被改变;Lua是按照以下的步骤决定是返回nil还是其它值得:

    1.当访问一个table的字段时,如果table有这个字段,则直接返回对应的值;
    2.当table没有这个字段,则会促使解释器去查找一个叫__index的元方法【应该说是table的元表的__index元方法】,接下来就就会调用对应的元方法,返回元方法返回的值;
    3.如果没有这个元方法,那么就返回nil结果。
  在实际编程中,__index元方法不必一定是一个函数,它还可以是一个table。
  当它是一个函数时,Lua以table和不存在key作为参数来调用该函数,这就和上面的代码一样;
  当它是一个table时,Lua就以相同的方式来重新访问这个table

  __metatable方法:

    local set = {}
    setmetatable(set, mt)
    mt.__metatable = "You cannot get the metatable" -- 设置完我的元表以后,不让其他人再设置

  PS:不要搞混了元表和元方法:
    元方法是元表里的函数,local a = {};a.__index = function()...这个__index还不是a的元方法!,当a以元表的身份出现时才是,比如local b={},setmetatable(b,a),虽然“这个__index还不是a的元方法”的说法有失偏颇,但可以帮忙理解,赋值元方法是元表的方法,不是母表的。

2.

index:取下标操作用于访问 table[key] 。

function gettable_event (table, key)
local h
if type(table) == "table" then
local v = rawget(table, key)
if v ~= nil then return v end
h = metatable(table).__index --记住,是元表的__index方法,不是table的__index方法,所以元表一般都定义了__index
if h == nil then return nil end
else
h = metatable(table).__index
if h == nil then
error(···);
end
end
if type(h) == "function" then
return h(table, key) -- 调用处理器
else return h[key] -- 或是重复上述操作
end
end

__call元方法的调用时机:
> function f(tb,x,y) return x+y+tb.n end
> b={}
> b.__call = f
> a = {}
> a.n=100
> setmetatable(a,b)
> print(a(1,2)) --103,有点构造函数的味道,但却明显不是!只是执行一个函数,没有创建实例,不过__call经常被赋值成构造函数,这时就是了。

3.

require用于使用模块,module用于创建模块
require:
require一个模块:
  1.先判断package.loaded这个table中有没有对应模块的信息;
  2.如果有,就直接返回对应的模块,不再进行第二次加载;
  3.如果没有,就加载,返回加载后的模块
  4.搜索package.path的所有路径下,该模块名的Lua文件是否存在,不存在则搜索package.cpath的所有路径下的,该模块名的C程序库文件是否存在,还不存在就报错返回了,当找到了这个文件以后,如果这个文件是一个Lua文件,它就通过loadfile来加载该文件;如果找到的是一个C程序库,就通过loadlib来加载,这两都只加载不运行
  5.require会将返回值存储到table package.loaded中
  6.require以模块名为参数 运行 已经加载的文件
  7.如果运行没有返回值(即return 结尾),require就会返回table package.loaded中的值
  注:require一个c程序库时,lua会去调用该库的luaopen_xx函数:require "mLualib"调用luaopen_mLualib函数,具体是:
    local path = "mLualib.dll"
    local f = package.loadlib(path,"luaopen_mLualib") -- 返回luaopen_mLualib函数
    f() -- 执行
    可以看一下lua层调用c层dll的例子。
module:
module("player_t")内部做了:
  1.模块名设为player_t;
  2.建立一个空table;
  3.在全局环境_G中添加模块名对应的字段,将空table赋值给这个字段;
  4.在已经加载table中设置该模块;
  5.设置环境变量。(该模块就不能访问_G环境了)
module(..., package.seeall):
  这句话的功能就好比module(...)再加上了setmetatable(M, {__index = _G}),可以正常访问_G
一个典型的使用例子:

  module( "drop_item_t", package.seeall )--创建一个drop_item_t的模块(在require这个脚本的时候创建)

  drop_item_mt = drop_item_mt or { __index = drop_item_t }--创建一个元表,它的__index指向drop_item_t这个全局表(这个表是module语句中创建的),是为了使用drop_item_t表里的函数
  setmetatable( drop_item_t, { __index = ctrl_t } )--drop_item_t元表设为ctrl_t,其中ctrl_t是另外一个全局表,这句的意义是让drop_item_t继承ctrl_t表,是为了让drop_item_mt使用ctrl_t的函数
  function new( _item_id )
    local drop_item = {}
    setmetatable( drop_item, drop_item_mt ) --创建一个表,设元表为drop_item_mt,则这个表可以使用表drop_item_t和表ctrl_t的所有函数
    return drop_item
  end

  注:这里我一开始也迷惑__index的使用,但看了官方文档即前面第2点的代码后就知道,元表只是“中介”,真正起作用的是元表中的元方法,如__index用于搜寻

4.

闭包是由函数和与其相关的引用环境组合而成的实体,即
闭包=函数+引用环境,多个闭包之间木有联系和影响,他们引用的是不同的环境

function Fun1()
local iVal = 10 -- upvalue
function InnerFunc1() -- 内嵌函数
print(iVal) --
end

function InnerFunc2() -- 内嵌函数
iVal = iVal + 10
end
return InnerFunc1, InnerFunc2
end

-- 将函数赋值给变量,此时变量a绑定了函数InnerFunc1, b绑定了函数InnerFunc2
local a, b = Fun1()--生产一个闭包
local c, d = Fun1()--又生产一个闭包
-- 调用a
a() -->10

-- 调用b
b() -->在b函数中修改了upvalue iVal

-- 调用a打印修改后的upvalue
a() -->20
--调用c,不受第一个闭包影响
c() -->10

闭包一个重要用途是用于迭代

5.

协程:
  协程的resume和yield配合起来,在主程序和协同程序之间交换数据:resume处于主程中,它将外部状态(数据)传入到协同程序内部;而yield则将内部的状态(数据)返回到主程中。
  create后第一次resume称为启动协程,后面的resume叫重新唤醒协程,而每个yield都是一样的。resume分成“开始resume”,和“resume返回”;yield分成“遇到yield”和“重启yield”
  当resume是启动协程时,开始resume就是运行创建协程时传入的函数,resume返回 则返回true/false(出错时false),和 遇到yield时yield的参数:

co = create(f());
f(){return yield(1,2,3)}
resume(co);--返回true,1,2,3,即遇到yield把协程数据传到主线程
--当resume是重新唤醒协程时,开始resume就是重启yield,这时,resume的第2,3...个参数会借助yield传入协程中:
co = create(f());
f(){local a, b = yield(1,2,3);print(a,b);}
resume(co);返回true,1,2,3
resume(co,"a","b");//print(a,b)打印的结果是"ab",即resume把数据传入了协程中,重启yield是入口,协助了数据的传入。

  一个“遇到yield把协程数据传到主线程”的例子:
  生产者消费者模式:

function produter()
local i=0
while true do
i=i+1
yield(i)
end
end
co = create(produter);
local consume = resume(co);--每次resume(co)都能生产一个产品(i)

  一个“开始resume即重启yield把主线程数据传入协程”的例子:
  协程缓存:有一个任务需要不定时执行,并且这个任务需要协程执行:

co = coroutine.create(function (f)
while f do
f = coroutine.yield(f())
end
end)

  每次resume就执行一次任务,但只需create一个协程,即协程缓存。

6.

弱表的使用例子:能解决,缓存的内存开销大过带来的性能提升,问题

function memoize (f)
local mem = {} -- 缓存化表
setmetatable(mem, {__mode = "kv"}) -- 设为弱表
return function (x) -- ‘f’缓存化后的新版本
local r = mem[x]
if r == nil then --没有之前记录的结果?
r = f(x) --调用原函数
mem[x] = r --储存结果以备重用
end
return r
end
end

7.

lua和c/c++交互:强烈建议看这篇博文:http://www.cnblogs.com/sevenyuan/p/4511808.html

因为行文简单明了:简单而没有废话,行文思想也很有趣,但有几句话不妥:

  1.类似的还有lua_setfield,设置一个表的值,肯定要先将值出栈,保存,再去找表的位置。

    应该是先设置完值后,才出栈,不然干嘛压栈,不就为了在lua层new一个变量,然后给表设置使用吗?,出栈就毁了。

  2.lua_getglobal(L,"var")会执行两步操作:1.将var放入栈中,2.由Lua去寻找变量var的值,并将变量var的值返回栈顶(替换var)。

    应该是先把_G压栈...

8.待续。。。

时间: 2024-10-12 19:40:26

Lua学习的相关文章

Lua学习笔记(七):迭代器与泛型for

1.迭代器与闭包 迭代器是一种支持指针类型的结构,它可以遍历集合的每一个元素.在Lua中我们常常使用函数来描述迭代器,每次调用该函数就返回集合的下一个元素. 迭代器需要保留上一次成功调用的状态和下一次成功调用的状态,也就是他知道来自于哪里和将要前往哪里.闭包提供的机制可以很容易实现这个任务.记住:闭包是一个内部函数,它可以访问一个或者多个外部函数的外部局部变量.每次闭包的成功调用后这些外部局部变量都保存他们的值(状态).当然如果要创建一个闭包必须要创建其外部局部变量.所以一个典型的闭包的结构包含

Lua学习 1) —— Android调用变量取值与赋值

2014-07-08 Lua脚本语言,嵌入在App中扩展开发是很不错的. 关于Android与Lua的环境搭配,我直接下载别人编好的.so与.jar(放到libs下就好了) 下面简单介绍一下Android调用Lua中的变量以及赋值 LuaState mLuaState; mLuaState = LuaStateFactory.newLuaState(); mLuaState.openLibs();//加载库 mLuaState.LdoString("x = 101");//执行一段lu

Lua学习(2)——表达式

1. lua算术操作符lua支持的算数操作符: + - * /除 ^指数 %取模 -符号 2. lua关系操作符 <小于 >大于 <= >= == ~=不等于 3. 逻辑操作符 and,or,not 所有逻辑操作符将false,nil 视为假,其他视为真. >print(4 and 5) 5 max = (x>y) and x or y 类似于C语言中选择表达式 max = x>y?x:y 4. 字符串连接 ..表达式 > print("Hello

Lua学习——table

table类型实现了“关联数组”.“关联数组”是一种具有特殊索引方式的数组.不仅可以通过证书来索引它,还可以使用字符串或其他类型(除了nil)来索引它.table是Lua中主要的数据结构机制(事实也是仅有的),具有强大的功能.基于table可以以一种简单.统一和高效的方式来表示普通数组.符号表.集合.记录.队列和其他数据结构. table的特性: table是一个“关联数组”,数组的索引可以是数字或者是字符串 table 的默认初始索引一般以 1 开始 table 的变量只是一个地址引用,对 t

Lua学习(4)——函数

在Lua中函数的调用方式和C语言基本相同,如:print("Hello World")和a = add(x, y).唯一的差别是,如果函数只有一个参数,并且该参数的类型为字符串常量或table的构造器,那么圆括号可以省略,如print "Hello World"和f {x = 20, y = 20}.    Lua为面对对象式的调用也提供了一种特殊的语法--冒号操作符.表达式o.foo(o,x)的另一种写法是o:foo(x).冒号操作符使调用o.foo时将o隐含的作

(原创)cocos2d-x 3.0+ lua 学习和工作(2) : 单一继承简单介绍

-- 星月相随倾心贡献~~~ -- 本章简单介绍一下:单一继承 -- 多继承本人还没有用过,主要是lua多继承感觉不好用~~~个人感觉~~~大汗~! -- example: local Base = class( "Base" ) Base.__index = Base function Base:ctor(...) print( self.__cname ) -- 输出:类名字.class( "xxx" ), self._cname 就是 xxx end func

Lua学习笔记9:多文件

一 终端中执行多个文件:-l 加入在文件一中定义了一个变量,在另一文件中输出这个变量,代码如下: --file1.lua num = 100 --file2.lua print(num) 终端输入(注意:不是lua命令行): lua -lfile1 -lfile2 注意:不要加上文件后缀名.lua 二 命令行中加载文件 --lib.lua function norm(x, y) local n2 = x^2 + y^2 return math.sqrt(n2) end function twic

lua学习笔记10:lua简单命令行

前面多次用了命令行,这次就好好学下命令行: 一 格式 lua [options][script][args] 二 具体命令 -e 直接将命令传个lua -l 加载一个文件 -i 进入交互模式 例如,终端输入: lua -e "print(math.sin(12))" lua学习笔记10:lua简单命令行,布布扣,bubuko.com

lua学习笔记11:lua中的小技巧

lua中的小技巧,即基础lua语言本身的特种,进行一个些简化的操作 一 巧用or x = x or v 等价于: if not x then x = v end 如果x为nil或false,就给他赋值为 二 三元运算符实现 a and b or c 类似C语言: a ? b : c and 的运算由优先级高于or lua学习笔记11:lua中的小技巧,布布扣,bubuko.com

LUA学习之路--初识LUA

LUA在葡萄牙语中是“月亮”的意思.1993年由巴西的Ponifical Catholic University开发.该语言是由一个来自计算机图形技术组织的团队开发,并作为自由软件发行.LUA开发小组的目标是开发一种小巧.高效并且能够很好的和C语言一起工作的编程语言.在脚本语言领域,LUA是最快.最高效的脚本语言之一. LUA和传统的脚本语言不同,它是一种易整合语言(glue language).一般的脚本语言用于控制执行重复的任务,而易整合语言可以让使用者把其他语言的功能整合在一起.这样就让脚