• Lua程序设计(4th) 第三部分 语言特性


    第18章 迭代器和泛型for

       18.1 迭代器和闭包

        首先编写一个简单的迭代器

    -- 工厂
    local values = function(t)
        local i = 0
        return function() 
            i = i + 1;
            return t[i]
        end
    end
    
    t = {10, 20, 30}
    iter = values(t) -- 调用工厂,创建一个闭包,即迭代器
    while true do
        local element = iter() -- 调用迭代器
        if element == nil then
            break
        end
        print(element)
    end

        不过使用泛型 for 更简单 。 毕竟,泛型 for 正是为了这种迭代而设计的 。

    t = {10, 20, 30}
    --内部保存了迭代函数,因此不需要变量iter
    for element in values(t) do
        print(element)
    end

        另一个示例如下。一般尽管迭代器本身有点复杂,但使用却很简单。

    function allwords ()
        local line = io.read() -- current line
        local pos = 1        -- current position in the line
        return function ()   -- iterator function
            while line do    -- repeat while there are lines
                local w, e = string.match(line, "(%w+)()", pos)
                if w then    -- found a word?
                    pos = e  -- next position is after this word
                    return w -- return the word
                else
                    line = io.read() -- word not found; try next line
                    pos = 1          -- restart from first position
                end
            end
            return nil       -- no more lines: end of traversal
        end
    end
    
    for word in allwords() do
        print(word)
    end

    18.3 无状态迭代器

        可以在多个循环中使用同一个这种不保存任何状态的迭代器,从而避免创建新闭包的开销 。

    local function iter (t, i)
        i = i + 1
        print("iter i = "..i)
        local v = t[i]
        if v then
            return i, v
        end
    end
    
    local function ipairs (t)
        -- 返回:迭代函数、不可变状态表、控制变量的初始值
        return iter, t, 0
    end
    local tbl = {111, 222, nil, 555}
    for k, v in ipairs(tbl) do
        --不断调用 iter(t, i)直至返回值为nil
        print(k.." "..v)
    end

         输出

    i = 1
    1 111
    i = 2
    2 222
    i = 3

         函数 pairs 与函数 ipairs 类似,但函数 pairs 的迭代函数是 Lua 语言中的一个基本函数 next:

    local function pairs (t)
        return next, t, nil
    end
    local tbl = {111, 222, nil, 555}
    for k, v in pairs(tbl) do
    --for k, v in next, tbl do
        --不断调用 next(t, k) 获得返回值 k, v,直至v为nil
        print(k.." "..v)
    end

         输出

    1 111
    2 222
    4 555

        按字母顺序输出函数名

    local lines = {
        ["luaH_set"] = 10,
        ["luaH_get"] = 24,
        ["luaH_present"] = 48,
    }
    a = {}
    for n in pairs(lines) do
        a[#a + 1] = n
    end
    table.sort(a)
    for _, n in ipairs(a) do print(n) end

         输出

    luaH_get
    luaH_present
    luaH_set

        使用迭代函数按字母顺序输出函数名,复杂性被隐藏到了迭代器中

    local lines = {
        ["luaH_set"] = 10,
        ["luaH_get"] = 24,
        ["luaH_present"] = 48,
    }
    function pairsByKeys (t, f)
        local a = {}
        for n in pairs(t) do
            a[#a + 1] = nend
        table.sort(a, f)
        local i = 0
        return function ()
            i = i + 1
            return a[i], t[a[i]]
        end
    end
    for name, line in pairsByKeys(lines) do
        print(name, line)
    end

         输出

    luaH_get    24
    luaH_present    48
    luaH_set    10

     第20章 元表和元方法

        元表中的元方法(一个函数)可以定义一个值在面对一个未知操作时的行为

    local Set = {}
    local mt = {}
    -- create a new set with the values of a given list
    function Set.new (l)
        local set = {}
        setmetatable(set, mt) -- 设置元表
        for _, v in ipairs(l) do set[v] = true end
        return set
    end
    function Set.union (a, b)
        if getmetatable(a) ~= mt or getmetatable(b) ~= mt then
            error("attempt to 'add' a set with a non-set value", 2)
        end
        local res = Set.new{}
        for k in pairs(a) do res[k] = true end
        for k in pairs(b) do res[k] = true end
        return res
    end
    function Set.intersection (a, b)
        local res = Set.new{}
        for k in pairs(a) do
            res[k] = b[k]
        end
        return res
    end
    -- presents a set as a string
    function Set.tostring (set)
        local l = {}
        -- list to put all elements from the set
        for e in pairs(set) do
            l[#l + 1] = tostring(e)
        end
        return "{" .. table.concat(l, ", ") .. "}"
    end
    
    --设置关系运算符的元方法
    mt.__le = function (a, b) -- subset
        for k in pairs(a) do
            if not b[k] then return false end
        end
        return true
    end
    mt.__lt = function (a, b) -- proper subset 真子集
        return a <= b and not (b <= a)
    end
    mt.__eq = function (a, b)
        return a <= b and b <= a
    end
    
    --设置算术运算符元方法
    mt.__add = Set.union --元表中加入元方法(metamethod)__add
    mt.__mul = Set.intersection  --元表中加入元方法(metamethod)__mul
    --设置库定义相关的元方法
    mt.__tostring = Set.tostring
    
    s1 = Set.new{10, 20, 30, 50}
    s2 = Set.new{30, 1}
    print(getmetatable(s1))      --两个表使用的是同一个元表mt
    print(getmetatable(s2))
    print(s1) -- print 会调用 tostring, tostring 调用 Set.tostring
    print(s1 + s2)       -->{1, 20, 30, 10, 50}
    print(s1 * s2)  -->{30}
    --s5 = s1 + 5 --报错并显示堆栈信息
    
    s1 = Set.new{2, 4}
    s2 = Set.new{4, 10, 2}
    print(s1 <= s2) --> true
    print(s1 < s2)  --> true
    print(s1 >= s1) --> true
    print(s1 > s1)  --> false
    print(s1 == s2 * s1) --> true
    
    mt.__metatable = "not you business" --设置之后,用户既不能看到也不能修改集合的元表
    print(getmetatable(s1)) --getmetatable会返回__metatable字段
    --setmetatable(s1, {}) -- error mt.__metatable字段已设置,s1不能再修改元表

         表相关的元方法:__index 和 __newindex

    -- create the prototype with default values
    prototype = {x = 0, y = 0, width = 100, height = 100}
    local mt = {} -- create a metatable
    -- declare the constructor function
    function new (o)
        setmetatable(o, mt)
        return o
    end
    mt.__index = function (_, key)
        return prototype[key]
    end
    mt.__newindex = function (_, key, v)
        return
    end
    w = new{x=10, y=20}
    print(w.x)     --> 10
    print(w.width) --> 100 --w.width 不存在,使用 __index 检索原型并返回结果
    mt.__index = prototype
    print(w.width) --> 100 --w.width 不存在,使用 prototype.width 作为结果
    w.newkey = 100 -- 使用 __newindex 对 w.newkey 赋值
    print(w.newkey) --> nil 

          具有默认值的表

    local key = {} --唯一的键, 避免命名冲突
    local mt = {__index = function (t) return t[key] end}
    function setDefault (t, d)
        t[key] = d
        setmetatable(t, mt)
    end
    tab1 = {x=10, y=20}
    print(tab1.z) --> nil
    setDefault(tab1, 0)
    print(tab1.z) --> 0 -- 对表 tab1 中不存在字段的访问都将调用它的 __index 元方法返回 d
    tab2 = {x=10, y=20}
    setDefault(tab2, 1) -- 使用相同的元表 mt,但有各自的默认值 t.___
    print(tab2.z) --> 1
    print(tab1.z) --> 0

         跟踪对表的访问

    function track (t)
        local proxy = {} -- proxy table for 't'
        -- create metatable for the proxy
        local mt = {
            __index = function (_, k)
                print("*access to element " .. tostring(k))
                return t[k] -- access the original table
            end,
            __newindex = function (_, k, v)
                print("*update of element " .. tostring(k) .. " to " .. tostring(v))
                t[k] = v -- update original table
            end,
            __pairs = function ()
                return function (_, k) -- iteration function
                    local nextkey, nextvalue = next(t, k)
                    if nextkey ~= nil then
                    -- avoid last value
                        print("*traversing element " .. tostring(nextkey))
                    end
                    return nextkey, nextvalue
                end
            end,
            __len = function () return #t end
        }
        setmetatable(proxy, mt)
        return proxy
    end
    
    t = {[2] = "hi"} -- the original table
    print(t[2])
    -- track 返回 proxy, t 变成 proxy 且一直为空表
    -- 但是 track 会追踪原始的 t
    t = track(t)
    -- t 即 proxy 一直为空表,因此t[2] 会触发 __index用于跟踪表的访问
    -- 但最终返回的值是原始表t中的值
    print(t[2])
    -- 赋值同上
    t[2] = "hello" --> *update of element 2 to hello
    print(t[2])
    --> *access to element 2
    --> hello
    
    t = track({10, 20})
    print(#t) --> 2
    for k, v in pairs(t) do print(k, v) end
    --> *traversing element 1
    --> 1 10
    --> *traversing element 2
    --> 2 20

         只读的表

    function readOnly (t)
        local proxy = {}
        local mt = {
        -- create metatable
        __index = t,
        __newindex = function (t, k, v)
        error("attempt to update a read-only table", 2)
        end
        }
        setmetatable(proxy, mt)
        return proxy
    end
    
    days = readOnly {
        "Sunday", "Monday", "Tuesday", "Wednesday",
        "Thursday", "Friday", "Saturday"
    }
    print(days[1]) --> Sunday
    days[2] = "Noday" --> stdin:1: attempt to update a read-only table

    第21章 面向对象编程

        可以参考基于原型的语言 ( prototype-based language ) 中的 一些做法来在 Lua 语言中模拟类,只需要创建一个专 门被用作其他对象(类 的实例 )的原型对象即可 。如果有两个对象 A 和 B ,要让 B 成为 A 的一个原型,只需要

    setmetatable(A, {__index = B})
        在此之后, A 就会在 B 中查找所有它没有的操作。如果把 B 看作对象 A 的类, 则只不过是术语上的一个变化 。
        类的使用示例:
    --
    Account = {
        balance = 0, -- 默认值
        withdraw = function (self, v)
            if v > self.balance then error "insufficient funds" end
            self.balance = self.balance - v
        end
    }
    
    function Account:deposit (v)
        self.balance = self.balance + v
    end
    
    function Account:new (o)
        o = o or {} -- create table if user does not provide one
        self.__index = self --隐藏的参数 self 得到的实参是 Account
        setmetatable(o, self)
        return o
    end
    
    --给 a.balance 赋了初始的金额。 a 有了自己的 balance 字段,后面对 a.balance 的访问就不会再涉及元方法了
    a = Account:new{balance = 0}
    
    --实际上调用的是 a.deposit(a, 300.00),冒号不过是语法糖
    --Lua 语言无法在表 a 中找到字段"deposit", 所以它会在元表的 __index 中搜索
    --因此等价于调用 getmetatable(a).__index.deposit(a, 300.00)
    --也就是 Account.deposit(a, 300.00),a作为 self 参数。因此继承了 Account.deposit 及其他字段
    a:deposit(300.00)
    a:withdraw(100.00)
    print(a.balance) --> 200
    
    --继承
    SpecialAccount = Account:new()
    --s 的元表是SpecialAccount
    s = SpecialAccount:new{limit = 1000.00}
    --Lua 语言无法在表 s 中找到字段 "deposit", 在元表的 __index(即 SpecialAccount) 中仍然找不到, 
    --最终在 Account 中 找到 deposit 的实现
    s:deposit(600.00)
    
    function SpecialAccount:withdraw (v)
        if v - self.balance >= self:getLimit() then
            error "insufficient funds"
        end
        self.balance = self.balance - v
    end
    
    function SpecialAccount:getLimit()
        return self.limit or 0
    end
    
    s:withdraw(800.00)
    print(s.balance) --> 500
    
    --一种多重继承的实现
    -- 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
        local parents = {...} -- list of parents
        -- class searches for absent methods in its list of parents
        setmetatable(c, {__index = function (t, k)     --(1)
            return search(k, parents)
        end})
        setmetatable(c, {__index = function (t, k)     --(2)
            local v = search(k, parents)
            t[k] = v -- save for next access
            return v
        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 c --返回新类
    end
    
    Named = {}
    function Named:getname ()
        return self.name
    end
    function Named:setname (n)
        self.name = n
    end
    
    --NA 同时继承 Account 和 Named
    NA = createClass(Account, Named) --即 c
    acc = NA:new{name = "Paul"}      --即 o
    -- acc:getname 不存在, 因此查找 acc.__index, 即 NA
    -- NA.getname 也不存在, 因此查找 NA.__index, 即 search(k), parents)
    -- 由于多继承的搜索具有一定的复杂性,因此使用(2)t[k]保存搜索结果以提升性能,再次访问就会像访问局部变量一样快了
    print(acc:getname()) --> Paul

        私有性

    function newAccount (initialBalance)
        local self = {balance = initialBalance}
        local withdraw = function (v)
            self.balance = self.balance - v
        end
        local deposit = function (v)
            self.balance = self.balance + v
        end
        local getBalance = function() return self.balance end
        return {
            withdraw = withdraw,
            deposit = deposit,
            getBalance = getBalance
        }
    end
    
    acc1 = newAccount(100.00)
    -- 只能通过函数访问内部状态表 self
    acc1.withdraw(40.00)
    print(acc1.getBalance()) --> 60
    
    function newAccount (initialBalance)
        local self = {
            balance = initialBalance,
            LIM = 10000.00,
        }
        local extra = function ()
            if self.balance > self.LIM then
                return self.balance * 0.10
            else
                return 0
            end
        end
        local getBalance = function ()
            return self.balance + extra()
        end
        return {
            getBalance = getBalance
        }
    end
    
    acc1 = newAccount(100000.00)
    -- 用户无法访问 extra 函数,extra 即为私有方法
    print(acc1.getBalance()) --> 110000

        一个在内部保存了状态的迭代器就是一个单方法对象。一种情况是,这个方法其实是一个根据不同的参数完成不同任务的分发方法。一种原型实现如下:

    function newObject (value)
        return function (action, v)
            if action == "get" then return value
            elseif action == "set" then value = v
            else error("invalid action")
            end
        end
    end
    
    -- 每个对象使用一个闭包,要比使用一个表的开销更低
    -- 虽然无法实现继承,但却可以拥有完全的私有性:
    d = newObject(0)
    print(d("get")) --> 0
    d("set", 10)
    print(d("get")) --> 10
         实现私有性的另一种方式是对偶表示:不仅可以通过数值和字符串来索引一个表,还可以通过任何值来索引

    一个表,尤其是可以使用其他的表来索引一个表。例如,在银行账户的实现中,可以把所有账户的余额放在表 balance 中

    -- 如果表 balance 是一个在模块 Account 内部保存的局部变量、
    -- 那么只有模块内部的函数才能访问它 。 因此保证了它的安全性。
    --这种实现的一大缺陷是: 一旦我们把账户作为表 balance 中的键,那么这个账户对于垃圾收集器而言就永远也不会变成垃圾
    local balance = {}
    Account = {}
    function Account:withdraw (v)
        balance[self] = balance[self] - v
    end
    function Account:deposit (v)
        balance[self] = balance[self] + v
    end
    function Account:balance ()
        return balance[self]
    end
    function Account:new (o)
        o = o or {}     -- create table if user does not provide one
        setmetatable(o, self)
        self.__index = self
        balance[o] = 0  -- initial balance
        return o
    end
    --对偶表示无须修改即可实现继承
    --We use this class just like any other one:
    a = Account:new{}
    a:deposit(100.00)
    print(a:balance())
     
     
     
     
     
     
  • 相关阅读:
    揭秘重度MMORPG手游后台性能优化方案
    算法:贪心、回溯(su)、分治、动态规划,思想简要
    表单提交 curl和浏览器方式
    mysql 聚集索引,非聚集索引,覆盖索引区别。
    虚拟机中的Linux不能上网
    第二篇 界面开发 (Android学习笔记)
    第一篇 入门必备 (Android学习笔记)
    C/C++知识补充 (1)
    向后/向前引用, 零宽断言, 转义, 命名分组
    C/C++知识补充(2) C/C++操作符/运算符的优先级 & 结合性
  • 原文地址:https://www.cnblogs.com/yyqng/p/14459591.html
Copyright © 2020-2023  润新知