rev |
line source |
jbe@0
|
1 seqlua: Extended sequences and iterators in Lua
|
jbe@0
|
2 ===============================================
|
jbe@0
|
3
|
jbe@0
|
4 This is an experimental package to extend Lua in the following manner:
|
jbe@0
|
5
|
jbe@0
|
6 * allow ipairs(...) to accept tables as well as functions or iterator triplets,
|
jbe@0
|
7 * provide a function iterator(...) that returns single functions unmodified,
|
jbe@0
|
8 but converts
|
jbe@0
|
9 * iterator triplets into closures, and
|
jbe@0
|
10 * tables into a function closure that iterates over the elements,
|
jbe@0
|
11 * provide the auxiliary C functions and macros to simplify iterating over both
|
jbe@0
|
12 tables and iterator functions with the same statement.
|
jbe@0
|
13
|
jbe@0
|
14 This library completely ignores the ``__ipairs`` metamethod (as it is
|
jbe@0
|
15 deprecated since Lua 5.3.0-alpha). It respects, however, any ``__call``
|
jbe@0
|
16 metamethods (this may cause unexpected behavior when passing callable tables
|
jbe@0
|
17 to ``ipairs``).
|
jbe@0
|
18
|
jbe@0
|
19
|
jbe@0
|
20
|
jbe@0
|
21 Lua part of the library
|
jbe@0
|
22 -----------------------
|
jbe@0
|
23
|
jbe@0
|
24 The new ``ipairs(...)`` function works as follows:
|
jbe@0
|
25
|
jbe@0
|
26 require "seqlua"
|
jbe@0
|
27
|
jbe@0
|
28 t = {"a", "b", "c"}
|
jbe@0
|
29
|
jbe@0
|
30 for i, v in ipairs(t) do
|
jbe@0
|
31 print(i, v)
|
jbe@0
|
32 end
|
jbe@0
|
33 -- prints:
|
jbe@0
|
34 -- 1 a
|
jbe@0
|
35 -- 2 b
|
jbe@0
|
36 -- 3 c
|
jbe@0
|
37
|
jbe@8
|
38 function alphabet(from, to)
|
jbe@0
|
39 local letter = nil
|
jbe@0
|
40 return function()
|
jbe@0
|
41 if letter == nil then
|
jbe@8
|
42 letter = from
|
jbe@8
|
43 elseif letter == to then
|
jbe@0
|
44 return nil
|
jbe@0
|
45 else
|
jbe@0
|
46 letter = string.char(string.byte(letter) + 1)
|
jbe@0
|
47 end
|
jbe@0
|
48 return letter
|
jbe@0
|
49 end
|
jbe@0
|
50 end
|
jbe@0
|
51
|
jbe@8
|
52 f = alphabet("a", "z")
|
jbe@0
|
53
|
jbe@0
|
54 for i, v in ipairs(f) do
|
jbe@0
|
55 print(i, v)
|
jbe@0
|
56 end
|
jbe@0
|
57 -- prints:
|
jbe@0
|
58 -- 1 a
|
jbe@0
|
59 -- 2 b
|
jbe@0
|
60 -- 3 c
|
jbe@0
|
61 -- ...
|
jbe@0
|
62 -- 25 y
|
jbe@0
|
63 -- 26 z
|
jbe@0
|
64
|
jbe@8
|
65 c = setmetatable(
|
jbe@8
|
66 { iter = alphabet("a", "f") },
|
jbe@8
|
67 { __call = function(t) return t.iter() end }
|
jbe@8
|
68 )
|
jbe@8
|
69
|
jbe@8
|
70 for i, v in ipairs(c) do
|
jbe@8
|
71 print(i, v)
|
jbe@8
|
72 end
|
jbe@8
|
73 -- prints:
|
jbe@8
|
74 -- 1 a
|
jbe@8
|
75 -- 2 b
|
jbe@8
|
76 -- 3 c
|
jbe@10
|
77 -- 4 d
|
jbe@10
|
78 -- 5 e
|
jbe@10
|
79 -- 6 f
|
jbe@8
|
80
|
jbe@12
|
81 g = function()
|
jbe@12
|
82 coroutine.yield("Alice")
|
jbe@12
|
83 coroutine.yield("Bob")
|
jbe@12
|
84 for i = 1, 3 do
|
jbe@12
|
85 coroutine.yield("Person #" .. tostring(i))
|
jbe@12
|
86 end
|
jbe@12
|
87 end
|
jbe@12
|
88 h = coroutine.wrap(g)
|
jbe@12
|
89
|
jbe@12
|
90 for i, v in ipairs(h) do
|
jbe@12
|
91 print(i, v)
|
jbe@12
|
92 end
|
jbe@12
|
93 -- prints:
|
jbe@12
|
94 -- 1 Alice
|
jbe@12
|
95 -- 2 Bob
|
jbe@12
|
96 -- 3 Person #1
|
jbe@12
|
97 -- 4 Person #2
|
jbe@12
|
98 -- 5 Person #3
|
jbe@12
|
99
|
jbe@0
|
100 set = {apple = true, banana = true}
|
jbe@0
|
101 for i, k, v in ipairs(pairs(set)) do
|
jbe@0
|
102 print(i, k, v)
|
jbe@0
|
103 end
|
jbe@0
|
104 -- prints:
|
jbe@0
|
105 -- 1 banana true
|
jbe@0
|
106 -- 2 apple true
|
jbe@0
|
107 -- (order of "apple" and "banana" may vary)
|
jbe@0
|
108
|
jbe@0
|
109 The function ``iterator(...)`` may be used to convert any table, any function,
|
jbe@0
|
110 or any iterator triplet into a single function (possibly creating a closure):
|
jbe@0
|
111
|
jbe@2
|
112 require "seqlua"
|
jbe@2
|
113
|
jbe@0
|
114 function filter_strings(...)
|
jbe@0
|
115 nextvalue = iterator(...)
|
jbe@0
|
116 return function()
|
jbe@0
|
117 local value
|
jbe@0
|
118 repeat
|
jbe@0
|
119 value = nextvalue()
|
jbe@0
|
120 until value == nil or type(value) == "string"
|
jbe@0
|
121 return value
|
jbe@0
|
122 end
|
jbe@0
|
123 end
|
jbe@0
|
124
|
jbe@0
|
125 for i, v in ipairs(filter_strings{"Hello", true, "World"}) do
|
jbe@0
|
126 print(i, v)
|
jbe@0
|
127 end
|
jbe@0
|
128 -- prints:
|
jbe@0
|
129 -- 1 Hello
|
jbe@0
|
130 -- 2 World
|
jbe@0
|
131
|
jbe@0
|
132 tbl = {apple = true, banana = true, [1] = "array entry"}
|
jbe@0
|
133 for v in filter_strings(pairs(tbl)) do
|
jbe@0
|
134 print(v)
|
jbe@0
|
135 end
|
jbe@0
|
136 -- prints:
|
jbe@0
|
137 -- banana
|
jbe@0
|
138 -- apple
|
jbe@0
|
139 -- (order may vary)
|
jbe@0
|
140
|
jbe@0
|
141
|
jbe@0
|
142
|
jbe@0
|
143 C part of the library
|
jbe@0
|
144 ---------------------
|
jbe@0
|
145
|
jbe@0
|
146 In ``seqlualib.h``, the following macro is defined:
|
jbe@0
|
147
|
jbe@0
|
148 #define seqlua_iterloop(L, iter, idx) \
|
jbe@0
|
149 for ( \
|
jbe@0
|
150 seqlua_iterinit((L), (iter), (idx)); \
|
jbe@0
|
151 seqlua_iternext(iter); \
|
jbe@0
|
152 lua_pop((L), 1) \
|
jbe@0
|
153 )
|
jbe@0
|
154
|
jbe@0
|
155 This macro allows iteration over either tables or iterator functions (but not
|
jbe@0
|
156 iterator triplets) as the following example function demonstrates:
|
jbe@0
|
157
|
jbe@0
|
158 int printcsv(lua_State *L) {
|
jbe@0
|
159 seqlua_Iterator iter;
|
jbe@0
|
160 seqlua_iterloop(L, &iter, 1) {
|
jbe@0
|
161 if (seqlua_itercount(&iter) > 1) fputs(",", stdout);
|
jbe@0
|
162 fputs(luaL_tolstring(L, -1, NULL), stdout);
|
jbe@1
|
163 lua_pop(L, 1); // pops value that luaL_tolstring pushed onto stack
|
jbe@0
|
164 }
|
jbe@0
|
165 fputs("\n", stdout);
|
jbe@0
|
166 return 0;
|
jbe@0
|
167 }
|
jbe@0
|
168
|
jbe@11
|
169 printcsv{"a", "b", "c"}
|
jbe@11
|
170 -- prints: a,b,c
|
jbe@11
|
171
|
jbe@11
|
172 printcsv(assert(io.open("testfile")):lines())
|
jbe@11
|
173 -- prints: line1,line2,... of "testfile"
|
jbe@0
|
174
|
jbe@7
|
175 NOTE: ``seqlua_iterinit`` will store one extra element on the stack during
|
jbe@7
|
176 iteration. When ``seqlua_iternext`` returns 0, this extra element is popped
|
jbe@7
|
177 from the stack automatically.
|
jbe@3
|
178
|
jbe@0
|
179 Additionally, ``seqlualib`` includes a function ``seqlua_iterclosure(L, idx)``,
|
jbe@0
|
180 which converts a table at a given stack index into a function closure (stored
|
jbe@0
|
181 on the same stack index) that iterates over the elements of the table. If the
|
jbe@0
|
182 value at the given stack index is already a function, it leaves the value
|
jbe@4
|
183 unchanged. If the value is convertible to a function using ``__call,`` then the
|
jbe@7
|
184 value is replaced by a closure calling the ``__call`` metamethod.
|
jbe@0
|
185
|
jbe@0
|
186
|