Chapter 7Iterators and the Generic for

7.1 Iterators and Closures

An iterator is any construction that allows you to iterate over the elements of a
collection. In Lua, we typically represent iterators by functions:each time we
call the function, it returns the “next” element from the collection.靠,,每次都一个function call,但都是同一个Iterator,

because the Iterator is closure ,will keep the state for each iterator call.


Any iterator needs to keep some state between successive calls, so that it
knows where it is and how to proceed from there. Closures provide an excellent
mechanism for this task. Remember that a closure is a function that accesses
one or more local variables from its enclosing environment


Therefore, a closure construction  typically involves two functions: the closure itself and a factory, the function
that creates the closure plus its enclosing variables.


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


In this example, values is the factory. Each time we call this factory, it creates  a new closure (the iterator itself). This closure keeps its state in its external  variables t and i. Each time we call the iterator, it returns a next value from
the list t. After the last element the iterator returns nil, which signals the end  of the iteration.

iterator=values ({1,2,3,4}) --这样的一次调用,就返回的就是iterator,其实就是个function. 只是利用了closure featur.


t = {10, 20, 30}
iter = values(t) -- creates the iterator
while true do
local element = iter() -- calls the iterator
if element == nil then break end use gerneric for ,no need this statement
print(element)
end


However, it is easier to use the generic for. After all, it was designed for this
kind of iteration:
t = {10, 20, 30}
for element in values(t) do  --不需要判断nil,for 已经 帮你做了
print(element)
end




          for i=1,#a do  --generic for 语法
              sumResult=sumResult+a[i]*x^(i-1);
            end;

  


function MyFactory1 (tb)
  i=0;
  return function()
            i=i+1;
            return tb[i]
         end
end

function MyFactory2 (tb)
  i=0;
  maxI=#tb;
  return function()
            i=i+1;
            indxToreturn=i<maxI and i or nil;
            return indxToreturn, tb[i] -- indxToreturn and tb[i] 都是nil的时候, iterator for 才会停止迭代
         end
end

for kk in MyFactory1{1,4,6,7,} do  --iterator syntanx,其实不管是那中syntax,执行完 do body end 后,will  回到for  header ,然后numeric for ,步进一次, iterator for,will call the iterator function again. when the function return all nill,will stop .
  print(kk);
end

for kk,vv in MyFactory2{1,4,6,7,} do
  print(kk,vv);
end


myIetra2=MyFactory2{1,4,6,7,};
for kk,vv in myIetra2 do   --注意我们传递的是iterator function,not a function call, so can't writemyIetra2()
  print(kk,vv);
end




Listing 7.1. Iterator to traverse all words from the input file:

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 s, e = string.find(line, "%w+", pos)
                          if s then -- found a word?
                            pos = e + 1 -- next position is after this word
                            return string.sub(line, s, e) -- 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


7.2 The Semantics of the Generic for

We saw that the generic for keeps the iterator function internally,也就是for 自己refer 了一个iterator during the  loop.

myIt=MyFactory2{1,4,6,7,};

for k,v in myIt do ....end,  //其实不需要自己再keep 一个myIt 的Iterator,因为for 已经作了

Actually, it keeps three values: the iterator function, an invariant state,
and a control variable.
Let us see the details now.
The syntax for the generic for is as follows:
for <var-list> in <exp-list> do
<body>
end

var-list is a list of one or more variable names, separated by commas, and
exp-list is a list of one or more expressions, also separated by commas.More
often than not, the expression list has only one element, a call to an iterator
factory. 也就是exp-list 可以是多个expression,但通常都是一个,a call to an iterator factory.


for k, v in pairs(t) do print(k, v)  ... end

for line in io.lines() do
io.write(line, "\n")
end
We call the first variable in the list <var-list> the control variable. Its value is never nil
during the loop, because when it becomes nil the loop ends
.

function MyFactory2 (tb)
  i=0;
  maxI=#tb;
  return function()
            i=i+1;
            indxToreturn=i<maxI and i or nil;
            return indxToreturn, tb[i]
         end
end


function MyFactory3 (tb)
  i=0;
  return function()
            i=i+1;
            return tb[i] ,i; --compare MyFactory2 and 3, 其实只要把返回调一调,就不用在判断nil.因为table帮我们做了做了,first varible is the control variable.
         end
end


for kk,vv in MyFactory3{'a','b','d','f','g'} do
print(kk,vv);
end


The first thing the for does is to evaluate the expressions after the in. These  expressions in <exp-list> should result in the three values kept by the for: the iterator   function, the invariant state, and the initial value for the control variable. Like in a multiple assignment, only the last (or the only) element of the list can result  in more than one value; and the number of values is adjusted to three, extra  values being discarded or nils added as needed. (When we use simple iterators,   the factory returns only the iterator function, so the invariant state and the   control variable get nil.)in MyFactory3{'a','b','d','f','g'} 其实这样的一个简单调用只是返回了一个iterator function, 在in 后面还可以继续写express,让这些express 返回多两个value, one isinvariant state and the  control variable , 显然上面的那个simple express, 让另外的两个variable get nil 了,,,


After this initialization step, the for calls the iterator function with two  arguments: the invariant state and the control variable. (From the standpoint   of the for construct, the invariant state has no meaning at all. The for only  passes the state value from the initialization step to the calls to the iterator   function.) Then the for assigns the values returned by the iterator function to   the variables declared by its variable list. If the first value returned (the one
assigned to the control variable) is nil, the loop terminates. Otherwise, the for  executes its body and calls the iteration function again, repeating the process. 好detail 的执行过程需要仔细的读,大概理解如下:

for k,v  in MyFactory3{'a','b','d','f','g'} do 我们把in ..do 之间的block 看成如下


do

local sysIterator=MyFactory3{'a','b','d','f','g'};

local invariantState=nil; --,由于in 后只有一个express,so these 2 variable get nil.

local controlVairable=nil; --到这里时initialization step finish.

当然也可以写成:

localsysIterator,invariantState,controlVairable =MyFactory3{'a','b','d','f','g'};


while true do

   local var1,var2,var3...varn=sysIterator(invariantState,controlVairable); --var1,var2,var3...varn,其实就是in 前面的k,v,

  只是这里写成通用的来表示for <var-list> in <exp-list> do 的 <var-list>,同时我们注意到 invariantState,controlVairable  这两个变量被传递入iterator 了,,,


 controlVairable=var1 -- 看第一个返回value assign to the  controlVairable,

  if controlVairable  then

  break

  end

  <block> --这个就是 for 的body,如果不跳出for ,就执行for body,,

end

end


通过这个detail process, we know why I writeMyFactory3, return table[i] first.



7.3 Stateless Iterators

As the name implies, a stateless iterator is an iterator that does not keep any
state by itself. Therefore, we can use the same stateless iterator in multiple
loops, avoiding the cost of creating new closures.


A stateless iterator generates the  next element for the iteration using only these two values. A typical example of
this kind of iterator is ipairs, which iterates over all elements of an array:

a = {"one", "two", "three"}
for i, v in ipairs(a) do
print(i, v)
end


The state of the iteration is the table being traversed (that is the invariant  state, which does not change during the loop), plus the current index (the control variable). Both ipairs (the factory) and the iterator are quite simple; we could
write them in Lua as follows:


local function iter (a, i)
   i = i + 1
   local v = a[i]
    if v then
       return i, v
      end
  end
function ipairs (a)
  return iter, a, 0  --compare with MyFactory3, 我们知道返回的闭包是不keep 住table and i 计数器的,而是利用了另外的我们一直忽略的变量 invariant  state ,the control variable,因为我们每次call iterator function,我们是要把这两个变量传递给iterator 的,so why not .何必要浪费这两个变量而在我们的iterator 里面自己create 呢,, so we call  this as  stateless iterator.

end


function MyFactory3 (tb)
  i=0;
  return function()
            i=i+1;
            return tb[i] ,i;
         end
end



The pairs function, which iterates over all elements of a table, is similar,  except that theiterator function is the next function, which is a primitive   function in Lua:

function pairs (t)
return next, t, nil
end

The call next(t,k), where k is a key of the table t, returns a next key in the
table
, in an arbitrary order, plus the value associated with this key as a second
return value.
The call next(t,nil) returns a first pair. When there are no more
pairs, next returns nil.


Some people prefer to use next directly, without calling pairs:
for k, v in next, t do  --也就是我们不一定要在这里 pass a iterator Factor, 一个函数也ok啊,由于我们会passinvariant  state ,the control variable,so next will work. Notic ,we write next, not next() ,funtion call,note the difference.

<loop body>
end


print('----------------->>>>')
tb2={
a="trevor",
c="cybar",
b="Krishan",
'a','b','e','f','k'

};

for k,v in next, tb2,nil do
 print (k,v);
end

print('===============');
for k,v in pairs (tb2) do   --pairs and next 只会返回key-value pairs
 print(k,v);
end

print('$$$$$$$$$$$$$$$$$$$$$');
for p,val in ipairs (tb2) do    --而ipairs,返回的sequence-value pairs,not return key-value pairs.
print (p,val);
end


An iterator to traverse a linked list is another interesting example of a  stateless iterator.
local function getnext (list, node)
   if not node then
     return list
    else
        return node.next
    end
   end
function traverse (list)
     return getnext, list, nil
end


The trick here is to use the list main node as the invariant state (the second
value returned by traverse) and the current node as the control variable. The
first time the iterator function getnext is called, node will be nil, and so the
function will return list as the first node. In subsequent calls, node will not be
nil, and so the iterator will return node.next, as expected. As usual, it is trivial
to use the iterator:


list = nil
for line in io.lines() do
list = {val = line, next = list}
end
for node in traverse(list) do
print(node.val)
end


==my version of stateless iterator


local itratorf=function (t,i)
      i=i+1;
      if  t[i] then
       return i,t[i]; --由于for 会把first return value assign to the control variable, and send it back to the next call,

               --so here we can't  return like that  return t[i],i. 这样t[i] ,就变成了下一个controll variable, i=i+1 时候会出问题。

       else
        return nil,nil;
       end
       
    end
function myFactory4(tb)
     return itratorf,tb,0
end

for i,v in myFactory4({5,6,7,8,9}) do
 print (i,v);
end




7.4 Iterators with Complex State

  invariant state and a control variable. 这两个变量其实我们只要利用好invariant state 这个变量,不但可以兼顾control variable 的角色,还可以有更复杂的control,办法就是invariant state set as a table type.

local iterator -- to be defined later
  function allwords ()
    local state = {line = io.read(), pos = 1} 通过table 返回complext state
  return iterator, state
end

function iterator (state)
  while state.line do -- repeat while there are lines
                                    -- search for next word
            local s, e = string.find(state.line, "%w+", state.pos)--%w+ ,alpha numeric searching..
            if s then -- found a word?
                         -- update next position (after this word)
              state.pos = e + 1
                  return string.sub(state.line, s, e) --return to for. next while wil start from e+1.
            else -- word not found
                      state.line = io.read() -- try next line...进入下一个while
                    state.pos = 1 -- ... from first position
         end
    end
   return nil -- no more lines: end loop
end


  typically a closure is  more efficient than an iterator using tables: first, it is cheaper to create a closure
than a table; second, access to non-local variables is faster than access to table  fields. Later we will see yet another way to write iterators, with coroutines. This is the most powerful solution, but a little more expensive.


7.5 True Iterators

上面描述的for 的执行机制我们知道其实真正做iterator 的是for ,而不是所谓的iterator function. there is a way to build a true iterator.

function allwords (f)
   for line in io.lines() do
    for word in string.gmatch(line, "%w+") do  --用了两个for 循环,虽然思路看起来好象清晰一些,但麻烦啊
           f(word) -- call the function
     end
end
end

allwords(print)


True iterators were popular in older versions of Lua, when the language
did not have the for statement.


Exercises

Exercise 7.1: Write an iterator fromto such that the next two loops become
equivalent:
for i in fromto(n, m)
<body>
end

for i = n, m
<body>
end
Can you implement it as a stateless iterator?


for i=1,5 do   --相当于我们要实现自己的numeric for
print(i);
end

print ("==============");
local fromTo=function (t,f)
     f=f+1;
    if f<=t then
      return f;
    else
      return nil;
    end
end
local fromToFactory=function(f,t)
   return fromTo,t,f-1; --让from -1 ,可以perfectly 处理fromTo 里面f=f+1 会miss first number的问题
end

for i in fromToFactory(1,5) do
 print (i);
end


Exercise 7.2: Add a step parameter to the iterator from the previous exercise.
Can you still implement it as a stateless iterator?


print ("==============kkkk");
local fromTo=function (t,f,step)
      f=f+step;
    if f<=t then
       return f;
    else
      return nil;
    end
end
local fromToFactory=function(f,t,step)
   return   function (t,f) --只能这里create a closure, then this closure env will wrap step in to it's evn
            return fromTo(t,f,step); --remember the return keywords.
            end,t,f-step;
end

for i in fromToFactory(1,10,2) do
 print (i);
end


--stateless implement

use table instead..


















































































  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值