Lua for generics and iterators

Iterators and generics for

In this chapter we discuss the write iterator is normative for, we start from a simple iterator, then we learn how to write more efficient iterations by using the power of the normative for.

Iterators and closures

Iterator is a pointer type of supporting structure, which can traverse each element of the collection. In Lua we often use to describe an iterator function, each call to this function returns the next element of the collection.

Iterator needs to hold on a successful call, and the state of a successful call, that is, he will know from where and to where to go. Closures provide mechanisms can easily accomplish this task. Remember: closure is an internal function, it can access the local variables of the one or more external functions. After each successful call to the closure of these local variables are stored outside of their value (state). Of course, if you want to create a closure must create its external local variables. Therefore, the structure of a typical closure comprises two functions: one is the closure themselves; the other is a plant (closures creating function).

To take a simple example, we wrote a simple iterator is a list, and ipairs () difference is that we achieved this iterator returns the value of the element rather than an index subscript:

function list_iter (t) 
local i = 0 
local n = table.getn(t) 
return function () 
 i = i + 1 
 if i <= n then return t[i] end
end 
end 

List_iter this example is a factory, every call he would create a new closure (iterator itself). Local variables store internal closure (t, i, n), each time he returns a call to the element value in the list, when no value list, returns nil we can use this iterator in the while statement:

t = {10, 20, 30} 
iter = list_iter(t) -- creates the iterator 
while true do
local element = iter() -- calls the iterator 
if element == nil then break end
 print(element) 
end 

We designed this iterator can be easily used for normative statement

t = {10, 20, 30} 
for element in list_iter(t) do
 print(element) 
end 

It is normative for all iterations of the loop handle bookkeeping (bookkeeping): first calls iterative plant; internal retention iterated function, so no variable iter; iterator then calls the function in each new iteration; when the iterator returns nil when the loop ends (later we will see more normative for qualified tasks).

Let's look at a slightly more advanced example of that: we write a iterates over all matching words in a file. To achieve the object, we need to keep two values: the current row and the current row offset, we use two external local variables line, pos save these two values.

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 

The body portion string.find iterated function calls the function, in the current row string.find find a matching word from the current position, using the example word matching pattern '% w +' described; a word, if found, the iteration function updates pos current position to a position after the first word, and the word returned (string.sub substring function to extract parameters from a position between two of the line). Otherwise, the iterative function reads a new line and search again. If no end line-readable returns nil.

Although iterative function somewhat complicated, but using them is very intuitive:

for word in allwords() do
 print(word) 
end 

Typically, iterated function is immune to write use. This is not a big problem: general Lua programming does not need to define their own iteration function, but use unless absolutely need to define their own languages.

For semantic normative

We saw earlier that the iterator has a drawback: each time you need to create a closure call this approach in most cases no problem, such as creating a closure in allwords iterator price than to read the entire file He says insignificant, but to create the cost of closure in some cases can not be tolerated. In these cases we can use the state normative for itself to save iteration.

We saw earlier in Chengzhong Fan of saving for their own internal iterative function, in fact, it holds three values over the cycle: iterative function, status and control variables constant described in detail below.
Fan for the grammar of the following:

for <var-list> in <exp-list> do
 <body> 
end 

Is the name of one or more comma-separated list of variables, is an expression of one or more comma-separated list, usually exp-list only one value: iterative call the factory.

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

List of variables k, v; expression list pair (t), in many cases, the list of variables is only one variable, such as:

for line in io.lines() do
 io.write(line, '\n') 
end 

We call the list of variables in the first variable is the control variable, its value is nil ending cycle.

Here we look at the implementation process for the Fan of:
First, initialize, calculate the value of the expression in the back, the expression should return three values of the range required for: iterative function, status and control variables constant; and the same multi-value assignments If the number of results returned by the expression is less than three will automatically make up with nil, the extra part will be ignored.

Second, the state constants and variables as arguments to call the iterator control function (Note: for structure, the useless state constants, only captures his values ​​at initialization and passed to the iteration function).

Third, the function returns the value iteration assigned to the variable list.

Fourth, the first cycle is nil if the return is completed, otherwise the loop body is executed.

Fifth, the second step back to re-iterated function call.
More precise terms:

for var_1, ..., var_n in explist do block end
等价于
do 
local _f, _s, _var = explist 
while true do
 local var_1, ... , var_n = _f(_s, _var) 
 _var = var_1 
 if _var == nil then break end
 block 
end 
end 

If we are iterated function f, the constant state is s, the control variable initial value a0, the control variables cycle: a1 = f (s, a0), a2 = f (s, a1), ......, until ai = nil.

Iterator stateless

Stateless iterator iterator refers not retain any state, so we can use in the loop iterator avoid the creation of stateless closures spend an extra price.

Each iteration, iteration functions are invoked with the values ​​of two variables as parameters (state variables and control constants) of a stateless iterator using only these two values ​​may be acquired next element. A typical example of such a simple stateless iterator is ipairs, he through each element of the array.

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

Iteration state includes being traversed table (cycle does not change the state of constant) and the current index index (control variables), ipairs and iterative function is very simple, we can achieve in Lua:

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
end 

When Lua calls ipairs (a) to start the cycle, he acquires three values: ITER iterative function, status and control variables constant a is the beginning of the original method: an initial value 0; then Lua calls ITER (a, 0) returns 1, a [1] (unless a [1] = nil); call the second iteration iter (a, 1) returns 2, a [2] ...... until the first non-nil element.

Lua 库中实现的 pairs 是一个用 next 实现的原始方法:
function pairs (t) 
return next, t, nil
end 
还可以不使用 ipairs 直接使用 next 
for k, v in next, t do
 ... 
end 
记住:exp-list 返回结果会被调整为三个,所以 Lua 获取 next、t、nil;确切地说当他调用 pairs 时获取

Multi-State iterator

In many cases, the iterator state needs to save more information than a simple constant state and control variables, the most simple iterator, this time we are not using closures but with the use of two domains (line is very simple, he iteration function and must return the initial state: when the pick-pairs is to use a closure, there is a way to encapsulate all of the state information to the table, the table state constant as iterator, since in this case all the information is stored in the table, it is generally not required second iterated function parameter.

Let's rewrite allwords, pos) of the table.

开始迭代的函数
local iterator -- to be defined later 
function allwords() 
local state = {line = io.read(), pos = 1} 
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) 
 if s then -- found a word? 
 -- update next position (after this word) 
 state.pos = e + 1 
 return string.sub(state.line, s, e) 
 else -- word not found 
 state.line = io.read() -- try next line... 
 state.pos = 1 -- ... from first position 
 end 
end 
return nil -- no more lines: end loop 
end 

We should write as much as possible of stateless iterator, because the cycle time for the state to save, do not need to create a small price target spend; if you can not use stateless iterator implementation should use a closure as possible; do may not use the table in this way, because the cost of closure to create than to create a small table, if you can not achieve a stateless iterator, as far as possible the use of closures; do not use the table in this way as much as possible, because creating closures price than to create a small table, while Lua deal closure rate faster than the processing table. Later we will see another way to create an iterator use collaboration, this way more powerful but more complicated.

The real iterators

Iterator name somewhat misleading, because it does not iteration, iteration function is for complete sentence, perhaps a better name for it should be 'generator'; but in other languages ​​such as java, C ++ iterator argument is very common we will also follow this term.

There is a way to create a complete iterator internally. So that when we use an iterator not need to use the cycle; we just need to use every iteration processing tasks as calling the iterator can be.

Specifically, the iterative function accepts as a parameter a, and this function is called within the iterator.

As a specific example, the above-described manner we rewrite allwords iterator:

function allwords (f) 
-- repeat for each line in the file 
for l in io.lines() do
 -- repeat for each word in the line 
 for w in string.gfind(l, "%w+") do
 -- call the function 
 f(w) 
 end 
end 
end 
如果我们想要打印出单词,只需要
allwords(print) 
更一般的做法是我们使用匿名函数作为作为参数,下面的例子打印出单词'hello'出现
的次数:
local count = 0 
allwords(function (w) 
if w == "hello" then count = count + 1 end
end) 
print(count)for 结构完成同样的任务:
local count = 0 
for w in allwords() do
if w == "hello" then count = count + 1 end
end 
print(count) 

The real iterators style of writing is very popular in Lua old version was not yet a for loop.

Two styles of writing little difference, but there are differences: on the one hand, the second style is easier to write and understand;
on the other hand, for a more flexible structure, you can use the break and continue statements; in the true style of writing in the iterator the return statement just returned from anonymous function instead exit the loop.

Published 252 original articles · won praise 151 · views 10000 +

Guess you like

Origin blog.csdn.net/qq_39885372/article/details/104323852