美文网首页Cocos2d-X与游戏开发
Lua语言笔记(为热更新做铺垫)

Lua语言笔记(为热更新做铺垫)

作者: ZZ曾帅 | 来源:发表于2018-02-27 18:50 被阅读51次
    [前言:很多基本的语法之类的在笔记中没有提及,需要在看之前了解一下lua基本语法]
    -- ======lua简单知识======
    print(.02 + .01)-->0.03
    print(2 + "3")-->5
    print("2" + "6")-->8
    print("2" .. "3")-->23
    print("-2e2" * "6")-->-200*6
    print(157 .. 428)-->157428
    -- 数字不能和字符串+
    
    -- 匿名函数
    function test_func(func)
        func()
    end
    
    test_func(
        function()
            print("s")
        end
    )
    
    -- 可变参数(求平均值)
    function average(...)
        local s = 0
        local arg = {...} -- 这个就代表着传入的多个参数
        for k, v in pairs(arg) do
            s = s + v
        end
        print(s / #arg)
        return s / #arg
    end
    average(1, 1, 1, 2)
    
    -- 逻辑运算符
    print(true and false)
    print(true or false)
    print(not true)
    
    -- 字符串
    s = string.find("Hello Lua user", "Lua", 1) --最后的参数表示从第几个开始找
    print(s)
    
    -- string.format(字符串格式化)
    s = string.format("字符串格式化 %s %s", "A", "B")
    print(s)-->字符串 A B
    date = 2; month = 1; year = 2014
    print(string.format("日期格式化 %02d/%02d/%03d", date, month, year))
    
    -- 十进制格式化
    print(string.format("%.4f", 1 / 3)) -->精确到小数点后四位
    
    -- 案例
    tab = {
        string.format("%c", 83), --输出S
        string.format("%+d", 17.0), --输出 + 17
        string.format("%.5d", 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("%.3f", 13), --输出13.000
        string.format("%q", "One\nTwo"), --输出"One\Two"
        string.format("%s", "monkey"), --输出monkey
        string.format("%20s", "monkey"), --输出 monkey
        string.format("%.4s", "monkey") --输出 mon
    }
    
    for k, v in pairs(tab) do
        print(k, v)
    end
    
    -- 数字转换文字
    local function NumToCN(num)
        local size = #tostring(num)
        local CN = ""
        local StrCN = {"一", "二", "三", "四", "五", "六", "七", "八", "九"}
        for i = 1, size do
            CN = CN .. StrCN[tonumber(string.sub(tostring(num), i, i))]
        end
        return CN
    end
    print(NumToCN(56665))
    
    -- 模拟字符串分割
    function StrSplit(inputstr, sep)
        if sep == nil then
            sep = "%s"
        end
        local t = {}
        local i = 1
        for str in string.gmatch(inputstr, "([^"..sep.."]+)") do
            t[i] = str
            i = i + 1
        end
        return t
    end
    local a = "23245023496830,汉字。。。。"
    local b = StrSplit(a, ",")
    print(b[1])
    
    -- 数组 索引是默认从1开始,但是可以自定义为从别的数字开始
    array = {}
    
    for i = -2, 2 do
        array[i] = i * 2
    end
    
    for k, v in pairs(array) do
        print(k, v)
    end
    
    -- ======lua难点======
    -- 迭代器
    -- [1].无状态的迭代器
    -- 以下实例我们使用了一个简单的函数来实现迭代器,实现 数字 n 的平方:
    function square(iteratorMaxCount, currentNumber) --iteratorMaxCount(状态常量),currentNumber(控制变量)
        if currentNumber < iteratorMaxCount
            then
            currentNumber = currentNumber + 1
            return currentNumber, currentNumber * currentNumber
        end
    end
    
    for k, v in square, 3, 0
        do
            print(k, v)
        end
    
    -- ========实现简单的ipairs========
    -- tip:迭代的状态包括被遍历的表(循环过程中不会改变的状态常量)和当前的索引下标(控制变量),
    -- ipairs和迭代函数都很简单,我们在Lua中可以这样实现:
    -- 在迭代数组的时候,如果遇到nil则会停止迭代
    local tab = {1, 2, nil, 4, 5}
    function iter (a, i)
        i = i + 1
        local v = a[i]
        if v then
            return i, v
        end
    end
    
    function myipairs (a)
        return iter, a, 0
    end
    
    for i, v in myipairs(tab) do
        print(i, v)
    end
    
    print("-----对比------")
    
    for i, v in ipairs(tab) do
        print(i, v)
    end
    
    -- [2].多状态的迭代器(无法迭代有key的table)
    array = {"Lua", "Tutorial", 2, [4] = 22, key1 = "ss", key2 = "nn"}
    
    function elementIterator (collection)
        local index = 0
        local count = #collection
        -- 闭包函数
        return function ()
            index = index + 1
            if index <= count
                then
                --  返回迭代器的当前元素
                return collection[index]
            end
        end
    end
    
    for element in elementIterator(array)
        do
            print(element)
        end
    
    -- ========ipairs和pairs的异同(重要)========
    -- 栗子1:
    local tabFiles = {
        [1] = "test2", 
        [6] = "test3", 
        [4] = "test1"
    }
    for k, v in ipairs(tabFiles) do --输出"test2",在key等于2处断开
        print(k, v)
    end
    print("------------")
    
    -- 栗子2:
    local tabFiles = {
        [2] = "test2", 
        [6] = "test3", 
        [4] = "test1"
    }
    -- 什么都没输出,为什么?因为控制变量初始值是按升序来遍历的,
    -- 当key为1时,value为nil,此时便停止了遍历, 所有什么结果都没输出
    for k, v in ipairs(tabFiles) do 
        print(k, v)
    end
    print("------------")
    
    -- 栗子3:
    local tabFiles = {
        [2] = "test2", 
        [6] = "test3", 
        [4] = "test1"
    }
    for k, v in pairs(tabFiles) do --输出2 test2, 6 test3, 4 test1
        print(k, v)
    end
    print("------------")
    
    -- 栗子4:
    -- 输出前三个   备注:因为第四个key不是整数
    local tabFiles = {"alpha", "beta", [3] = "no", ["two"] = "yes"} 
    for i, v in ipairs(tabFiles) do 
        print(tabFiles [i]) 
    end 
    print("------------")
    for i, v in pairs(tabFiles) do --全部输出   
        print(tabFiles [i]) 
    end 
    
    -- ======table的操作======
    --[1] table.concat (连接)
    tab1 = {1, 2, 3, 4, 5}
    local concat_str_1arg = table.concat(tab1)
    print(concat_str_1arg)
    local concat_str_2arg = table.concat(tab1, ",")
    print(concat_str_2arg)
    local concat_str_3arg = table.concat(tab1, ",", 2)
    print(concat_str_3arg)
    local concat_str_4arg = table.concat(tab1, ",", 2, 4)
    print(concat_str_4arg)
    
    --[2] table.insert (插入)
    tab1 = {"a", "b", "c", "d"}
    table.insert(tab1, "e")
    print(table.concat(tab1))
    table.insert(tab1, 2, "1")
    print(table.concat(tab1))
    
    --[3] table.sort (排序,对数字和字母或者ASC码的都可以排序)
    -- 默认从小到大排序
    tab1 = {9, 5, 4, 3, 7, 11, 2, 6, 2}
    table.sort(tab1)
    print(table.concat(tab1, "<"))
    
    -- 自定义从大到小排序
    local function testSort(a, b)
        return a > b
    end
    table.sort(tab1, testSort)
    print(table.concat(tab1, ">"))
    
    -- 字符串排序
    tab2 = {"a", "b", "c"}
    table.sort(tab2)
    print(table.concat(tab2, "<"))
    
    -- 注意以及技巧:
    -- 当我们获取 table 的长度的时候无论是使用 # 还是 table.getn 其都会在索引中断的地方停止计数,而导致无法正确取得 table 的长度。
    -- 可以使用以下方法来代替:
    function table_leng(t)
        local leng = 0
        for k, v in pairs(t) do
            leng = leng + 1
        end
        return leng; 
    end
    
    tab = {1, key = 2, 3, 4}
    for k, v in pairs(tab) do
        print(k, v)
    end
    print(table.getn(tab))
    print(#tab)
    
    print(table_leng(tab))
    
    -- ======lua元表(超级重点)======
    --元表的两个函数 setmetatable getmetatable 下面是一个最简单的例子
    mytable = {}                          -- 普通表 
    mymetatable = {}                      -- 元表
    setmetatable(mytable,mymetatable)     -- 把 mymetatable 设为 mytable 的元表 
    --可以简化成
    mytable = setmetatable({}, {})
    
    -- 元方法
    -- [1] __index元方法
    -- 当你通过键来访问 table 的时候,如果这个键没有值,那么Lua就会寻找该table的metatable(假定有metatable)中的__index 键。
    -- 如果__index包含一个表格,Lua会在表格中查找相应的键。
    -- 案例1
    mytable = setmetatable({key = 1}, {__index = function (arg, key) --第一个参数为默认参数
        if(key == "key2") then
            return "metatablevalue"
        end
    end
    })
    print(mytable.key, mytable.key2)
    案例2
    mytable = setmetatable({key1 = 1}, {__index = {key2 = 2}})
    print(mytable.key1, mytable.key2)
    
    -- [2] __newindex元方法
    -- __newindex 元方法用来对表更新,__index则用来对表访问 。
    -- 当你给表的一个缺少的索引赋值,解释器就会查找__newindex 元方法:如果存在则调用这个函数而对普通表不进行赋值操作。
    -- 如果存在__newindex元方法并且普通表中已经含有该键, 则会优先对普通表进行赋值
    -- 以下实例演示了 __newindex 元方法的应用:
    metatable = {key2 = 1}
    mytable = setmetatable({mytablekey = 22}, {__newindex = metatable})
    mytable.key2 = 2
    print(mytable.key2, metatable.key2)
    
    -- [3] __add元方法(表相加)
    mytable = setmetatable({1, 2, 3}, {__add = function (mytable, newtable)
        for i = 1, #newtable do
            table.insert(mytable, #mytable + 1, newtable[i])
        end
        return mytable
    end
    })
    newtable = {4, 4, 4}
    mytable = mytable + newtable
    for k, v in pairs(mytable) do
        print(k, v)
    end
    
    -- 或者这样写
    local mt = {}
    mt.__add = function(a, b)
        return a.v + b.v
    end
    local a = {v = 10}
    local b = {v = 12}
    setmetatable(a, mt)
    print("a + b :", a + b)
    
    -- [4.1] __metatable元方法保护元表不被查看和修改
    mytable = setmetatable({1, 2, 3}, {__metatable = "you can not get the metatable"})
    print(getmetatable(mytable))
    
    或者这样写
    local mt = {}
    mt.__metatable = "you can not get the metatable"
    mytable = {}
    setmetatable(mytable, mt)
    print(getmetatable(mytable))
    
    -- [4.2]写一个只读的表,只需跟踪所有对table的更新操作,并引发一个错误即可
    function readOnly(table) 
        local proxy = {} 
        local mt = {
            __index = table, 
            __newindex = function(table, k, v) 
                error("attempt to update a read-only table") 
            end 
        } 
        setmetatable(proxy, mt) 
        return proxy 
    end 
    days = readOnly({"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"}) 
    print(days[1]) 
    days[2] = "Noday" 
    
    -- [5] 其余的操作符元方法
    -- __add对应的运算符 '+'
    -- __sub对应的运算符 '-'
    -- __mul对应的运算符 '*'
    -- __div对应的运算符 '/'
    -- __mod对应的运算符 '%'
    -- __unm对应的运算符 '-'
    -- __concat对应的运算符 '..'
    -- __eq对应的运算符 '=='
    -- __lt对应的运算符 '<'
    -- __le对应的运算符 '<='
    
    -- [6] __call元方法
    -- 当调用一个不是函数的值的时候会去查找call这个元方法,例如使用table作为方法(table())来调用的时候,call元方法则会被调用,
    -- func指代的是table本身的地址
    local mt = {}
    mt.__call = function(func, ...)
        print("call table like a function", func, ...)
    end
    local temp = {}
    setmetatable(temp, mt)
    temp(1, 2, 3)
    
    -- [7] __tostring 元方法
    -- __tostring 元方法用于修改表的输出行为,类似c#重写toString
    mytable = setmetatable({10, 20, 30}, {
        __tostring = function(mytable)
            sum = 0
            for k, v in pairs(mytable) do
                sum = sum + v
            end
            return "表所有元素的和为:" .. sum
        end
    })
    print(mytable)
    
    -- ======lua协同程序======
    -- coroutine.create()
    -- 创建coroutine,返回coroutine, 参数是一个函数,当和resume配合使用的时候就唤醒函数调用
    
    -- coroutine.resume()
    -- 重启coroutine,和create配合使用
    
    -- coroutine.yield()
    -- 挂起coroutine,将coroutine设置为挂起状态,这个和resume配合使用能有很多有用的效果
    
    -- coroutine.status()
    -- 查看coroutine的状态
    
    -- 注:coroutine的状态有三种:dead,suspend,running,具体什么时候有这样的状态请参考下面的程序
    
    -- coroutine.wrap()
    -- 创建coroutine,返回一个函数,一旦你调用这个函数,就进入coroutine,和create功能重复
    
    -- coroutine.running()
    -- 返回正在跑的coroutine,一个coroutine就是一个线程,当使用running的时候,就是返回一个corouting的线程号
    
    -- 使用下面的程序来了解一下这几个函数的基本用法:
    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) --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()) --nil
    print("----------")
    
    -- ======lua文件IO======
    ####### -- [[ 在此省略,与其他语言的文件操作类似
    
    -- ======lua垃圾回收机制======
    -- 检测lua内存泄漏的案例:
    
    collectgarbage("collect") --垃圾收集(释放内存)
    local s1 = collectgarbage("count") -- 垃圾统计(占用了多少内存(kb))
    print(">>>>>>>>>>>>>>>> count1", s1)
    local my_list = {}
    for i = 1, 1000, 1 do
        local v = {}
        v[1], v[2] = 1024 + i, 32 + i
        my_list[v[1]] = v[2]
    end
    print(">>>>>>>>>>>>>>>> count2", collectgarbage("count")) -- 局部变量(v)和my_list所占用的内存
    collectgarbage("collect")
    print(">>>>>>>>>>>>>>>> count3", collectgarbage("count")) -- 注:使用“collectgarbage("collect")”,
    局部变量v被回收,my_list没有被回收
    如果这里不置为空,则出现内存泄漏了
    my_list = nil
    collectgarbage("collect")
    print(">>>>>>>>>>>>>>>> count4", collectgarbage("count")) -- 销毁my_list之后被回收
    
    -- 总结一: 如何监测Lua的编程产生内存泄露:
    
    -- 1.       针对会产生泄露的函数,先调用collectgarbage("count"),取得最初的内存使用
    
    -- 2.       函数调用后, collectgarbage("collect")进行收集, 并使用collectgarbage("count")再取得当前内存, 最后记录两次的使用差
    
    -- 3.       从test1的收集可看到, collectgarbage("collect")被调用,并不保证一次成功, 所以, 大可以调用多次
    
    -- 总结二: 如何避免Lua应用中出现的内存使用过大行为:
    
    -- 1.       当然是代码实现不出现泄露
    
    -- 2.       在测试中,其实还发现, Lua中被分配的内存,其实并不会自动回收(个人估计要么就是Lua虚拟机没有做这个事情,
    -- 要么就是回收的时机是在C层), 所以, 为了避免内存过大, 应用的运行时,可能需要定期的(调用collectgarbage("collect"),
    -- 又或者collectgarbage("step"))进行显式回收。
    
    -- ======lua模拟面向对象======
    -- Q:如何定义对象的方法以及调用对象的方法?
    -- A:面向对象的特殊性在于它以this指针的方式传递了对象本身,并且这种操作是隐藏起来的。 
    -- 在Lua中使用:实现面向对象方式的调用。:只是一个语法糖,它同时在方法的声明与实现中增加一个名为self的隐藏参数(对象本身)。
    
    Account = {balance = 1000} -- 账户余额初始为1000。
    --[[ 取钱。
         使用面向对象的方式隐藏了"self"参数,
         "withdraw()"完整的参数列表是"Account.withdraw(self, v)"。]]
    function Account:withdraw(v)
        self.balance = self.balance - v
    end
    --[[ 使用面向对象的方式隐藏了"self"参数,
         实际传递给"withdraw()"的参数是"Account"和"100.00"。]]
    Account:withdraw(100.00)
    print(Account.balance) --> 900.0
    我们可以用.定义函数,而用:调用函数,或者反之亦然,只要我们正确的使用这个被隐藏的self参数。
    Account = {balance = 1000} -- 账户余额初始为1000。
    function Account.withdraw(self, v) -- 使用"."定义函数。
        self.balance = self.balance - v
    end
    Account:withdraw(100.00) -- 使用":"调用函数。
    print(Account.balance) --> 900.0
    -- 存钱。
    function Account:deposit(v) -- 使用":"定义函数。
        self.balance = self.balance + v
    end
    Account.deposit(Account, 600.00) -- 使用"."调用函数。
    print(Account.balance) --> 1500.0
    
    -- Q:如何实现类?
    -- A:类在面向对象语言中就好象一个模板,通过模板所创建的实例就具有模板中规定的特性。Lua中没有类的概念,
    -- 每一个对象规定自己的行为,每一个对象就是自己的实例。不过在Lua中模拟“类”并不难,我们可以用继承的概念,使用两个对象,
    -- 让其中一个对象作为另一个对象的“类”, 
    -- setmetatable(a, {__index = b}) -- b作为a的类,在a中找不到的方法都将去b中寻找。 
    -- 继续扩展银行账户的例子
    
    Account = {balance = 100} -- 账户余额初始为0。
    function Account:new (o)
        o = o or {} -- create object if user does not provide one
        setmetatable(o, self) -- 新对象的"metatable"是"Account"(或其子类)。
        self.__index = self -- 新对象"metatable"中的"__index"依旧是"Account"(或其子类)。
        return o
    end
    function Account:deposit(v)
        self.balance = self.balance + v
    end
    function Account:withdraw(v) 
        self.balance = self.balance - v
    end
    --[[ "account1"继承了"Account"中的特性,
         就好象"account1"是由"Account类"创建出来的一样。
         这一句代码后,设置了"account1"的"metatable"是"Account",
         "Account.__index"依旧是"Account"。]]
    account1 = Account:new()
    -- 这里打印的是"Account.balance","Account.balance"作为"Account"实例的默认值。
    print(account1.balance) --> 100
    print(Account.balance) --> 100
    --[[ "account1:deposit(500.00)",实际上调用的是"account1.deposit(a, 500.00)",
         "account1"中没有"deposit()",所以去找"account1"的"metatable"中的"__index",
         即"getmetatable(account1).__index",即"Account"。
         又因为"Account"是一个"table",所以"account1.deposit(a, 500.00)"
         相当于"Account.deposit(a, 500.00)"。]]
    account1:deposit(500.00)
    --[[ 这里打印的是"account1.balance",
         因为在"Account.deposit()"中为"self.balance"赋值的同时定义了"account1.balance"。]]
    print(account1.balance) --> 600
    print(Account.balance) --> 100
    
    -- Q:如何实现继承?
    -- A:上面的例子中已经初步展现了继承的方式,下面再使用一个更贴切的例子进行说明,
    
    Account = {balance = 0}
    
    function Account:new (o)
        o = o or {}
        setmetatable(o, self)
        self.__index = self
        return o
    end
    
    function Account:deposit (v)
        self.balance = self.balance + v
    end
    
    function Account:withdraw (v)
        -- 普通账户不可以透支。
        if v > self.balance then error"insufficient funds" end
        self.balance = self.balance - v
    end
    
    --[[ 信用卡账户继承自普通账户,可以透支。
         "CreditAccount"是"Account"的一个“实例”,
         但他同时也可以作为一个“类”以产生其他实例。]]
    CreditAccount = Account:new{limit = 1000.00}
    
    -- 为了让信用卡账户能够透支,需要重写"withdraw()"方法。
    function CreditAccount:withdraw (v)
        -- 信用卡账户在一定额度内可以透支。
        if v - self.balance >= self:getLimit() then
            error"insufficient funds"
        end
        self.balance = self.balance - v
    end
    
    function CreditAccount:getLimit ()
        return self.limit or 0
    end
    
    --[[ 新的实例也可以规定自己的限额。
         "CreditAccount"中没有"new()",实际调用的是"Account.new()"。]]
    creditaccount1 = CreditAccount:new{limit = 2000.00}
    --[[ "creditaccount1"中没有"deposit()",
         "CreditAccount"中没有"deposit()",实际调用的是"Account.deposit()"。]]
    creditaccount1:deposit(100.00)
    -- 此时调用的是"CreditAccount:withdraw()"。
    creditaccount1:withdraw(200.00)
    print(creditaccount1.balance)    --> -100.0
    
    -- Q:如何实现多重继承?
    -- A:将__index赋值为一个函数,函数中可以搜索想要找的表(“父类”)。 
    -- 还记得吗,当__index是个函数时,Lua调用它,以”table”和缺失的”key”作为参数。而当__index是一个”table”时,
    -- Lua直接以缺失的”key”作为它的”key”再次访问他(相当于拿着缺失的”key”在它这张”table”中寻找)。
    
    -- ------------------- "Account类" -------------------
    Account = {balance = 0}
    
    function Account:new (o)
        o = o or {}
        setmetatable(o, self)
        self.__index = self
        return o
    end
    
    function Account:deposit (v)
        self.balance = self.balance + v
    end
    
    function Account:withdraw (v)
        if v > self.balance then error"insufficient funds" end
        self.balance = self.balance - v
    end
    --------------------------------------------------
    
    -- look up for 'k' in list of tables 'plist'
    local function search(k, plist)
        for i = 1, #plist do
            local v = plist[i][k]    -- try 'i'-th superclass
            if v then return v end
        end
    end
    
    function createClass(...)
        local c = {}        -- new class
    
        for i = 1, select('#', ...) do
            -- arg = {[1] = Account, [2] = Named}。
            arg[i] = select(i, ...)
        end
    
        --[[ class will search for each method in the list of its
            parents ('arg' is the list of parents)]]
        setmetatable(c, {__index = function (t, k)
            return search(k, arg)
        end})
    
        -- prepare 'c' to be the metatable of its instances
        c.__index = c
    
        -- define a new constructor for this new class
        function c:new (o)
            o = o or {}
            setmetatable(o, c)
            return o
        end
    
        -- return new class
        return c
    end 
    
    Named = {}    -- 姓名类,记录账户的姓名。
    function Named:getname ()
        return self.name
    end
    
    function Named:setname (n)
        self.name = n
    end
    
    -- 创建一个新类"NamedAccount",其继承于"Account"与"Named"。
    NamedAccount = createClass(Account, Named)
    account = NamedAccount:new{name = "Paul"}
    --[[ "account"中没有"getname",
         所以找"account"的"metatable.__index",
         "metatable"是"NamedAccount",
         "NamedAccount"的"__index"还是"NamedAccount"。
         而"NamedAccount"中依旧没有"getname()",
         所以找"NamedAccount"的"metatable.__index",是个函数,
         所以Lua调用这个函数,并将"NamedAccount"和"getname"作为参数传入。
         函数中又调用"search()","search()"中首先在"Account"中找"getname",
         没找到,又在"Named"中找"getname",找到了,所以调用"getname()"。]]
    print(account:getname())     --> Paul
    
    多重继承的效率可能比单一继承的效率低很多(因为多了search()这个过程)。如果想提高效率的话可以将找到的父类的函数拷贝到子类中,
    
    setmetatable(c, {__index = function (t, k)
        local v = search(k, arg)
        t[k] = v    -- "Named.getname()"存储在了"account"中。
        return v
    end})
    
    -- 这样,除了第一次调用,之后的调用实际上是调用子类中拷贝过来的父类的函数,省去了search()的过程,
    -- 效率就高很多。但是这种做法也有缺陷,在程序运行起来之后,就很难改变父类中的方法了,因为即使更改了,
    -- 子类也不会继承过去(子类保存了父类原先的方法)。
    
    -- Q:如何定义私有成员或私有方法?
    -- A:使用两个”table”,其一存储私有成员,另一个存储公有成员和公有方法,两个”table”组成”Closure”,
    -- 私有”table”作为公有”table”的”Closure”被访问,私有方法直接存储在”Closure”中,
    
    function newAccount (initialBalance)
        -- 私有"table"。
        local self = {
            balance = initialBalance,    -- 余额。
            count = 0    -- 积分。
        }
    
        -- 私有方法,未导出到公有"table"中,外部无法访问。
        local addCount = function (v)
            self.count = self.count + v * 0.1    -- 消费的10%作为积分。
        end
    
        local withdraw = function (v)
            self.balance = self.balance - v
            addCount(v)
        end
    
        local deposit = function (v)
            self.balance = self.balance + v
        end
    
        local getBalance = function () return self.balance end
    
        local getCount = function () return self.count end
    
        -- 公有"table"。
        return {
            withdraw = withdraw,
            deposit = deposit,
            getBalance = getBalance, 
            getCount = getCount
        }
    end
    
    account = newAccount(100)
    print(account.balance)    --> nil
    print(account.count)    --> nil
    print(account.getBalance(), account.getCount())    --> 100 0
    account.deposit(500)
    print(account.getBalance(), account.getCount())    --> 600 0
    account.withdraw(100)
    print(account.getBalance(), account.getCount())    --> 500 10
    
    -- 附加:
    -- 1、Lua中的”table”与面向对象编程中的对象有很多相似的地方。像对象一样,”table”有成员变量;
    -- 像对象一样,”table”本身与其存储的值相互独立,特别在于两个不同的对象拥有相同的值,但他们依旧是不同的对象。
    -- 反之,一个对象在不同的时间可以有不同的值,但对象本身不会改变;像对象一样,”table”的生命周期与其被谁创建以及在哪儿被创建无关。 
    -- 2、Lua中的面向对象编程有意思的地方在于,当你需要增加新的方法时,既可以在“类”中添加,也可以在“实例”中直接添加。
    
    -- -- 以添加一个“增加积分”的方法为例。
    -- -- 在“类”中添加,适用于许多用户均需要该方法。
    -- function CreditAccount:addCount(count) ... end
    -- -- 在“实例”中直接添加,适用于少数用户需要该方法。
    -- creditaccount1 = CreditAccount:new()
    -- creditaccount1.addCount = function(self, count) ... end
    
    -- 3、Lua使用”table”表示对象,本身不提供私有性机制。Lua的设计初衷是针对于中小型程序,
    -- 通常他们是一个大型系统的一部分。因此Lua避免设计上的冗余以及过多的人为限制,如果对象中的某些成员你不该访问,
    -- 那么你最好就”just do not do it”。Lua提供给程序员“meta-mechanisms”,程序员可以通过他们模拟许多需要的特性。
    
    -- ======结束======

    相关文章

      网友评论

      本文标题:Lua语言笔记(为热更新做铺垫)

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