Step By Step (Lua iterator and generic)

Source: Internet
Author: User

1. iterator and Closure:
In Lua, The iterator is usually a function. Every time the iterator function is called, the "Next" element in the set is returned. Each iterator must maintain a certain state between each successful call so that it can know its location and the next time. From this point of view, the closure mechanism in Lua provides language support for this problem. See the following example:

1 function values (t)
2 local I = 0
3 return function ()
4 I = I + 1
5 return t [I]
6 end
7 end
8 t = {10, 20, 30}
9 it = values (t)
10 while true do
11 local element = it ()
12 if element = nil then
13 break
14 end
15 print (element)
16 end
17 -- another method based on foreach (generic)
18 t2 = {15, 25, 35}
19 for element in values (t2) do
20 print (element)
21 end
22 -- the output result is:
23 -- 10
24 -- 20
25 -- 30
26 -- 15
27 -- 25
28 -- 35

From the above application example, compared with the while method, the generic for method provides a clearer implementation logic. Because Lua saves iterator functions for us internally and calls the implicit internal iterator at each iteration until the iterator returns nil to end the loop.

2. Syntax of generic:
The iterator in the preceding example has an obvious disadvantage, that is, a new closure variable needs to be created for each loop. Otherwise, after the first iteration is successful, when the closure is used for a new for loop, it will exit directly.
Here we will explain in detail the mechanism of generic (for) in Lua, and then give an example of a stateless iterator for our understanding. If our iterator is implemented as a stateless iterator, it is not necessary to re-declare a new iterator variable for every generic (.
The syntax of generic (for) is as follows:
For <var-list> in <exp-list> do
<Body>
End
For ease of understanding, because <exp-list> in actual application usually only contains an expression (expr), for simplicity, the description here will only contain an expression, instead of expression list. Now we first give the expression prototype and example, such:

1 function ipairs2(a)
2 return iter,a,0
3 end

This function returns three values. The first is the actual iterator function variable, and the second is a constant object. Here we can understand it as the container to be traversed, the third variable is the initial value passed in when the iter () function is called.
Let's take a look at the implementation of the iter () function, for example:

1 local function iter(a, i)
2 i = i + 1
3 local v = a[i]
4 if v then
5 return i, v
6 else
7 return nil, nil
8 end
9 end

In the iterator function iter (), two values are returned, corresponding to the key and value of the table, where key (returned I) is nil, generic () the iteration is deemed to have ended. Next, let's take a look at the actual use cases, such:

1 function ipairs2 ()
2 return iter, a, 0
3 end
4
5
6 local function iter (a, I)
7 I = I + 1
8 local v = a [I]
9 if v then
10 return I, v
11 else
12 return nil, nil
13 end
14 end
15
16 a = {"one", "two", "three "}
17 for k, v in ipairs2 (a) do
18 print (k, v)
19 end
20 -- the output result is:
21 -- 1 one
22 -- 2 two
23 -- 3 three

In this example, the generic (for) method can be expanded to the following while loop-based method, for example:

1 local function iter (a, I)
2 I = I + 1
3 local v = a [I]
4 if v then
5 return I, v
6 else
7 return nil, nil
8 end
9 end
10
11 function ipairs2 ()
12 return iter, a, 0
13 end
14
15 a = {"one", "two", "three "}
16 do
17 local _ it, _ s, _ var = ipairs2 ()
18 while true do
19 local var_1, var_2 = _ it (_ s, _ var)
20 _ var = var_1
21 if _ var = nil then -- note that only nil is returned by the iterator function.
22 break
23 end
24 print (var_1, var_2)
25 end
26 end
27 -- The output result is the same as the preceding one.

3. Example of a stateless iterator:
The example here will implement the iterator Of The traversal table.

1 local function getnext (list, node) -- iterator function.
2 if not node then
3 return list
4 else
5 return node. next
6 end
7 end
8
9 function traverse (list) -- expression of generic ()
10 return getnext, list, nil
11 end
12
13 -- initialize the data in the linked list.
14 list = nil
15 for line in io. lines () do
16 line = {val = line, next = list}
17 end
18
19 -- in the form of a generic (for) Repeat chain table.
20 for node in traverse (list) do
21 print (node. val)
22 end

The trick here is to use the head node of the linked list as the constant state (the second value returned by traverse) and the current node as the control variable. When the iterator function getnext () is called for the first time, node is nil. Therefore, the function returns list as the first node. In subsequent calls, node is no longer nil, so the iterator returns node. next, until the nil node at the end of the linked list is returned, then the generic (for) will judge that the iterator traversal has ended.
It should be noted that the traverse () function and the list variable can be called repeatedly without creating a new closure variable. This is mainly because the iterator function (getnext) is implemented as a stateless iterator.

4. iterators with complex states:
In the implementation of the iterator described above, the iterator needs to save many states, but the generic (for) only provides constant states and control variables for saving states. The simplest way is to use closure. Of course, we also encapsulate all the information in a table and pass it to the iterator as a constant state object. Although the constant state variable itself is constant, that is, it will not be replaced with other objects during iteration, but whether the data contained in this object changes completely depends on the implementation of the iterator. For the moment, since a constant object of the table type already contains information on dependencies of all iterators, The iterator can ignore the second parameter provided by generic (. The following is an example. See the following code:

1 local iterator
2 function allwords ()
3 local state {line = io. read (), pos = 1}
4 return iterator, state
5 end
6 -- The iterator function will be a real iterator
7 function iterator (state)
8 while state. line do
9 local s, e = string. find (state. line, "% w +", state. pos)
10 if s then
11 state. pos = e + 1
12 return string. sub (state. line, s, e)
13 else
14 state. line = io. read ()
15 state. pos = 1
16 end
17 end
18 return nil
19 end

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.