lua进阶

作者: 小小青蛙不怕风吹雨打 | 来源:发表于2016-12-04 20:44 被阅读0次
    _G.print("hello lua, version is", _VERSION)
    

    lua官网
    在线运行代码

    table面向对象语法糖

    lua对table中的函数调用做了优化,使用起来像类方法,增加了个特殊变量self

    local t = {x=1,}
    t.Add = function(self)
        self.x = self.x + 1
    end
    t.Add(t)
    
    print(t.x) -- 2
    
    local t = {x=1,}
    function t:Add()
        self.x = self.x + 1
    end
    t:Add()
    print(t.x) -- 2
    

    两个例子效果一样。第二个算是语法糖,隐含定义了第一个参数变量self,方便使用。

    module模块

    lua提供了module来实现模块管理。

    local print = print -- 注意这行
    module("hello")
    
    msg = "hello world"
    function printHelloWorld()
        print(msg)
    end
    

    这儿的msg和printHelloWorld定义时没有使用local,但是他们不是简单的全局变量。
    他们是模块的全局变量,外部使用hello.printHelloWorld()

    例子中第一行的local print=print,在这儿是一定要加的,不然module里不是使用print
    有个简单的方法module("hello",package.seeall),这样module里可以访问所有的全局变量。

    闭包 closure

    lua里的闭包就是函数。特殊之处,在于闭包绑定了一些局部变量,lua实现里称为upvalue

    function get_closure()
     -- 这个局部变量被下面的函数保存了
     local x = 1
     local add = function() x = x + 1 end
     local get = function() return x end
     -- add和get使用的是同一个x
     return get,add
    end
    local get,add = get_closure()
    print(get()) -- 1
    add()
    print(get()) -- 2
    

    闭包绑定局部变量时是引用绑定,多个闭包可以共享同一个局部变量。

    元表 metatable

    lua提供了元表机制,可用于扩展lua的功能,如:模拟类,模拟原型。
    理解上就是用一个table描述另一个table的一些特定情况如何处理。

    
    t = {}
    print(t.x) -- nil
    mt = {
        -- 当table不存在某个key值时,调用这个函数
        __index = function(_,key)
            return key .. " not find"
        end,
    }
    setmetatable(t,mt)
    print(t.x) -- x not find
    
    setmetatable(t,{
        -- 当t中不存在某个key值,尝试从这个表中获取
        __index = {x = "x save in metatable"}
    })
    print(t.x) -- x save in metatable
    
    

    上面的例子演示了最常见的元表项__index,还有其他元表项。

    元表key 描述
    __index 读取table[key],值为nil,会检查这个元表项。
    是function,就获取函数返回值。不是,就递归读取。
    __newindex 写入table[key] = value,原值为nil,检查这个元表项。
    是function,调用之。不是,递归写入。
    __gc 垃圾回收时调用,常用于userdata类型,相当于析构函数
    __call 使用obj()时调用,如果obj不是函数的话
    __mode 值是个字符串。用于定义弱引用,弱引用不能阻止垃圾回收。
    里面有k,则key是弱引用;里面有v,则value是弱引用
    __concat 自定义..运算符
    __eq 自定义==运算符
    __add 自定义+运算符
    __len 自定义#运算符
    __lt 自定义<运算符
    ... 还有些其他的操作

    注意:

    • 在lua里setmetatable只能修改table类型的元表,可以在c里修改其他类型数据的元表
    • 在lua中存在元表保护模式,如果原来的元表有__metatable字段,那该元表不能被替换。

    协程 coroutine

    lua实现了协程。
    协程有些像线程,函数调用可以被中断,然后再从中断的地方继续执行。
    协程与线程不同的地方在于,同一时刻只有一个协程处于运行状态,没有互斥锁的使用。
    经典的生产者消费者的例子:

    
    producer = coroutine.create(function()
        for x = 1,2 do
            print("生产 --> 第" .. x .. "个产品")
            coroutine.yield(x)
        end
        print("生产者结束生产")
    end)
    
    function consumer()
        while true do
            local _,x = coroutine.resume(producer)
            if x then
                print("消费 <-- 第" .. x .. "个产品")
            else
                print("消费者离场")
                break
            end
        end
    end
    
    consumer()
    

    输出的结果:

    生产 --> 第1个产品
    消费 <-- 第1个产品
    生产 --> 第2个产品
    消费 <-- 第2个产品
    生产者结束生产
    消费者离场
    
    

    模拟类·综合应用

    使用table和闭包模拟类封装。

    function New(name)
        local obj = {}
        local _name = name or "default name"
    
        obj.PrintName = function ()
            print(_name)
        end
        obj.SetName = function (name)
            _name = name
        end
        return obj
    end
     
    local obj = New()
    obj.PrintName() -- default name
    obj.SetName("new name")
    obj.PrintName() -- new name
    
    

    使用元表模拟类,可以实现继承。下面的例子实现的简单的单继承。

    -- 单继承框架
    function class(class_name,base_class)
        local cls = {__name = class_name}
        cls.__index = cls
        function cls:new(...)
            local obj = setmetatable({}, cls)
            cls.ctor(obj,...)
            return obj
        end
        if base_class then
            cls.super = base_class
            setmetatable(cls,{__index = base_class})
        end
        return cls
    end
     
    -- 使用
    BaseClass = class("base")
    function BaseClass:ctor()
    end
    function BaseClass:Say()
        print(self.__name .. " say")
    end
     
    DerivedClass = class("derived",BaseClass)
     
    local base = BaseClass.new()
    local derived = DerivedClass.new()
    base:Say() -- base say
    derived:Say() -- derived say
    
    

    c扩展

    lua和c交互时使用栈来传递参数。简单例子演示

    static int abs(lua_State *L)                    // [-0, +1, -]
    {
        double n = luaL_checknumber(L, 1);
        n = (n > 0 ? n : -n);
        lua_pushinteger(L, n);
        return 1;// 返回值个数:1个
    }
     
    static const luaL_Reg libs[] = {
        {"abs",abs},
        {NULL,NULL}
    };
     
    LUALIB_API int luaopen_test(lua_State *L) {
        luaL_register(L, "test", libs);// test 是模块名
        return 1;
    }
    
    

    lua使用 print(test.abs(-1)),栈的变化


    注释[-0, +1, -]简述了栈的变化,出栈0个,入栈1个。-表示不抛异常。

    lua提供很多操作栈的API用于扩展,这儿讲的简单了,扩展阅读。

    lua的C API
    操作lua栈

    相关文章

      网友评论

        本文标题:lua进阶

        本文链接:https://www.haomeiwen.com/subject/exihmttx.html