lua系统学习13-自定义创建一个迭代器

使用Closure特性自己创建迭代器

function IteratorDIY(t1)
    function iteratorTableValue(t)
        local i=0
        return function () i=i+1; return t[i] end
    end 
    for i in iteratorTableValue(t1) do
    print(i)
    end

end
IteratorDIY({0,1,2,3,4,5,6,'\\0'});

 

0
1
2
3
4
5
6
\0

 

在上面案例中 我们使用了Closure的特性,在一个函数内部定义了局部变量,在尾调用处返回一个匿名函数 在匿名函数中调用了这个在IteratorDIY中的局部变量。还记得之前说到Closure的时候,说这样的写法在匿名函数中会把在IteratorDIY的局部变量看作一个非局部的变量。对于函数内的匿名函数来说它既不是全局变量也不是局部变量,但对于IteratorDIY函数来说却又是局部变量。

接着我们使用for 接收变量 in initializeList do end

在这个示例中initializeList 会返回一个执行函数。这里呢,执行函数会在每次循环前被执行并把输出结果返回给接收变量,直到执行函数返回nil的时候 也就接收变量=nil的时候结束循环。

通过这样我们就实现了一个迭代器。当然我们也可以模拟实现IPair

function IteratorDIY2(t1)
   function iteratorTablePair(t)
       local i=0
       return function () i=i+1;
           if i>#t then
               return nil,nil
           else
               return i,t[i]
           end
       end
   end
   for i,v in iteratorTablePair(t1) do
       print(i,v)
   end
end
IteratorDIY2({0,1,2,3,4,5,6,'\\0'});

for in 循环结束的条件 必须要第一个接收变量为nil的时候才结束
上例中使用条件判断 当“当前累计的i大于长度的时候就返回nil”。

迭代io中所有单词与数字

function IteratorAllWords()

    function wordIterator()
        for word in allWords() do
        print(word)
        end
    end
    function allWords()
        local line=io.read()--他本身也是个迭代器
        local pos=1--- 开始位置
        return function()
            while line do
               local s,e= string.find(line,"%w+",pos)
                if s then
                    pos=e+1---s是字符串的开始索引 e是字符串的长度
                    return string.sub(line,s,e)
                else
                    line=io.read()
                    pos=1
                    if line=='quit' then
                        return nil
                    end
                end
            end
            return nil
        end
    end
    wordIterator()
end
IteratorAllWords()
asdsa23.fg
quit


asdsa23
fg

以上的这些示例 都是通过Closure会保存非局部的变量的特性实现的。但是如果每次循环迭代都会创建一个新的Closure就意味着要考虑性能问题了,那么有没有什么好的方案代替呢?
下面不使用Closure,利用For in会接收函数结果的特性来创建一个迭代器。

利用for-in特性创建无状态迭代器

function ForVar(t)
    function iteratorTablePair(t,i)
        return function () i=i+1;
            if i>#t then
                return nil,nil
            else
                return i,t[i]
            end
        end
    end
    i=0
    for i,v in iteratorTablePair(t,i) do
        print(i,v)
    end

end
    ForVar({2,3,4,5})
1	2
2	3
3	4
4	5

前面说过For varList in initializeList do

for在初始化时实际上会保存着initializeList执行的3个值:1.一个迭代器函数,2.恒定变量(表),3.一个控制变量。迭代器工厂用于返回一个迭代器函数,恒定变量在这里就是表,是为for提供的数据源。上面这个案例里,initializeList直接返回了迭代器函数,恒定变量和控制变量没有返回,所以默认为nil,但不影响运行。因为我们已经将t和i的引用了传入initializeList 当中了,for每次循环都会把t和i参数自动代入迭代器函数中运行。
varlist会接收迭代器函数的结果 当varlist[0]的结果为nil,循环为nil。所以我们把varlist中第一个变量称之为循环的控制变量,控制循环的结束。
上面迭代单词的示例中使用的是Closure的非局部变量的保存来实现迭代器,在这个示例中我们可以通过参数传值的方式代替Closure的非局部变量。利用到Closure无非就是能保存上一次变量计算的值,那么我们直接把返回过来的值再传回去不就好了。所以就有了 for i in func(t,i) do的实现。其实还可以更简洁,for in其实在循环过程内部保存了迭代器函数
用Ipair来举例 实际lua中的ipair不会像我们上面写的那些例子那样,它不会有closure的开销。

可能解释的不够清楚,看一下下面这个ipair示例就明白了

function ipairNoColsureImplent()
   local function  ipairIter(t,i)
       i=i+1
       local v=t[i]
       if v then
           return i,v
       end
    end

    function dummyIpair(t)
        return ipairIter,t,0
    end
    
    t={"a","b","c"}
    for i,v in dummyIpair(t) do
        print(i,v)
    end
    
end
ipairNoColsureImplent()

 

1	a
2	b
3	c

dummyIpair返回3个值,第一个是迭代器函数(for每次循环要执行的函数)、第二个是恒定变量,第三是控制变量。
实际上dummyIpair在for初始化时候执行而不是参与循环时候执行,真正参与循环的是dummyipair返回的函数。所以在dummyipair中返回恒定变量、控制变量 与 for varlist in ipairItera,恒定变量,控制变量 do。 这样的写法是一样的。因为在for in do 中 in的优先级是最高的,可以理解为迭代器初始化,然后才进行循环。
示例:

function ipairNoColsureImplent2()
    local function  ipairIter(t,i)
        i=i+1
        local v=t[i]
        if v then
            return i,v
        end
    end

    t={"a","b","c"}
    for i,v in ipairIter,t,0 do
        print(i,v)
    end
end

我们可以称dummyipair为迭代器的工厂,用来创建迭代器的。
在lua中会把恒定变量与控制变量的值,作为参数在第一次执行迭代器即ipairIter函数时候传入进去。
因为迭代器会返回控制变量的值,所以后面for in会自动把恒定变量和控制变量i再作为参数传入迭代器进行迭代。(此步骤是由for in 传入参数,不是从迭代器的工厂传入)

pair的实现

function pairNoColsureImplent()
    function dummyPair(t)
        return next,t,nil
    end
    t={"a","b","c",1,2}
    for i,v in dummyPair(t) do
        print(i,v)
    end
end
pairNoColsureImplent()

pair中用到了lua自带的next函数。用来访问table下一个元素
就有点像地址下移似的。这个直接通过操作指针应该也是可以的。

复杂的迭代器

作者:Miracle
来源:麦瑞克博客
链接:https://www.unitymake.com/archives/programming-life/lua/3455
本博客所有文章除特别声明外,均采用CC BY-NC-SA 4.0许可协议,转载请注明!
THE END
分享
打赏
海报
lua系统学习13-自定义创建一个迭代器
使用Closure特性自己创建迭代器 function IteratorDIY(t1) function iteratorTableValue(t) local i=0 return function () i=i+1; ret……
<<上一篇
下一篇>>
文章目录
关闭
目 录