当前位置:   article > 正文

(5)LUA程序设计-迭代器(state iterator & stateless iterator)_程序设计 stateless状态

程序设计 stateless状态

1.迭代器与closure的关系

所谓迭代器,就是对一个集合的遍历,比如遍历一个table数组,我们必须在每遍历完一个元素之后,我们必须记住下一个元素的索引。

前几节我们学过了闭合函数,它存在的非局部的变量,就是能够在多次调用闭合函数(返回的函数+非局部的变量)后,保留非局部变量的值,利用这一点,我们可以很容易的自定义一个迭代器

比如遍历一个t = {12,23,15,19}数组,自定义迭代器如下

function values(t)

   local i = 0;

 return function() i = i+1 return t[i]  end;

end

遍历方式有两种while  xxx  do ... end      或者用本节讲的for来遍历,

t = {12,23,15,19};

前者方面

iter = values(t);

while true  do

  local elem = iter();

 if nil == elem then break end;

 print(elem);

end

后者方法,采用for

for elem in values(t) do

   print(elem);

end

两种方法,显然for比较简洁一些,for会多次调用<expr-list>(泛型for格式:for <val-list> in <expr-list> do ... end)所处的迭代器(称为迭代工厂也行)

下面举一个更为复杂的迭代器,遍历 输入文件当中的单词,必须用两个变量来记住当前读到的文字所处行line,以及当前的列的位置pos

首先创建一个迭代器

function allwords()

 local line = io.read(); --读取一行文本

 local pos = 1;--初始当前为第一列的位置

 return function()

     while line do   -- 至少有一行文本

        local s,e = stirng.find(line,"%w+",pos) --调用string.find方法,第一个参数为当前文本串,第二个参数为正则表达式,表示单词,第三个参数表示,从line的第pos列开始查找

        if s then  -- 找到了单词的情况

            pos = e+1;   -- 移动pos的索引位置

            return string.sub(line,s,e); --节取line从s到e索引对应的单词,并返回

        else

              line = io.read()  --如果上一行文本内已经找不到单词了,再读取一行

              pos = 1--重新将pos置到第一列

        end

     end

return nil  --没有文本,返回空

 end

end

  我们用for可以简化代码,实现遍历如下:

  for word in allwords() do

    print(word)

 end

2  强大的泛型for

我们应该正确理解LUA提供的FOR泛型的强大之处,

我们知道,泛型for格式:for <val-list> in <expr-list> do ... end,正常情况下<val-list>这个变量列表可以是一个或多个,一般是三个,而<expr-list>也可以是一个或多个表达式,但一般情况是一个.

上面所举的两个例子(遍历数组,遍历文件当中的单词,我们称之无状态的迭代器,为什么?)有时候,我们不想迭代器本身保留着一些状态,或者控制变量,而希望迭代器本身不存在什么状态,而是将诸如控制变量,恒定状态等信息从迭代器中分离出来,这时候,我们就需要泛型for了

泛型for支持每次调用迭代工厂后,返回迭代器的一些状态给<val-list>,即for ...  in ... do ... end在处理时,首先处理in后面的表达式,一般处理后返回三个值,分别是:迭代函数(工厂)func,恒定状态 list,控制变量 index 这三个值,之后,利用返回的后两个值(list,index作为func函数的参数,调用func,返回给in之前的变量列表<var-list>)

举例:(无状态迭代器,状态交给了泛型for)还是以遍历数组为例(下面的代码其实是LUA ipairs方法的实现原理)

t = {12,23,15,19}

function values(t,i)

   i = i+1;

 return function() return i , t[i]  end;

end

function iter(t)

   return values,t,0

end

定义了上面的iter迭代器之后,我们可以这样子遍历数组了

for i,v in iter(t) do

print(i,v);

end

3较为复杂的有状态迭代器

我们将本节1当中举的例子(代码如下)有状态迭代器,修改成较复杂的有状态迭器

修改前:

function allwords()

 local line = io.read(); --读取一行文本

 local pos = 1;--初始当前为第一列的位置

 return function()

     while line do   -- 至少有一行文本

        local s,e = stirng.find(line,"%w+",pos) --调用string.find方法,第一个参数为当前文本串,第二个参数为正则表达式,表示单词,第三个参数表示,从line的第pos列开始查找

        if s then  -- 找到了单词的情况

            pos = e+1;   -- 移动pos的索引位置

            return string.sub(line,s,e); --节取line从s到e索引对应的单词,并返回

        else

              line = io.read()  --如果上一行文本内已经找不到单词了,再读取一行

              pos = 1--重新将pos置到第一列

        end

     end

return nil  --没有文本,返回空

 end

end

修改后:

function allwords()

 local  state = {line = io.read(),pos = 1}  --将(读取一行文本--初始当前为第一列的位置)的数据放到一个table里边,名为state

 return myIterator,state

end

function myIterator(state)

     while state.line do   -- 至少有一行文本

        local s,e = stirng.find(state.line,"%w+",state.pos) --调用string.find方法,第一个参数为当前文本串,第二个参数为正则表达式,表示单词,第三个参数表示,从line的第pos列开始查找

        if s then  -- 找到了单词的情况

           state.pos = e+1;   -- 移动pos的索引位置

            return string.sub(state.line,s,e); --节取line从s到e索引对应的单词,并返回

        else

              state.line = io.read()  --如果上一行文本内已经找不到单词了,再读取一行

              state.pos = 1--重新将pos置到第一列

        end

     end

return nil  --没有文本,返回空

 end

修改后的遍历跟修改前一样

  for word in allwords() do

    print(word)

 end

-------------------迭代器就讲到这里,下一节将讲解-(6)LUA程序设计-编译执行与错误(compile 、run & error)处理------------

LUA技术交流群,请加Q群:139315537,加入请注明来源。

本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号