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..