seqlua

annotate README @ 36:4f82d3b3dbd9

Removed wrong spaces in example output in README
author jbe
date Sun Aug 24 22:32:57 2014 +0200 (2014-08-24)
parents 332216604f83
children adf671e3458c
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@30 6 * allow ``ipairs(seq)`` to accept tables as well as functions (i.e function
jbe@30 7 iterators),
jbe@32 8 * add a new function ``string.concat(separator, seq)`` to concat either
jbe@32 9 table entries or function return values,
jbe@23 10 * provide auxiliary C functions and macros to simplify iterating over both
jbe@0 11 tables and iterator functions with the same statement.
jbe@0 12
jbe@33 13 Existing ``__ipairs`` or ``__index`` (but not ``__len``) metamethods are
jbe@33 14 respected by both the Lua functions and the C functions and macros. The
jbe@32 15 ``__ipairs`` metamethod takes precedence over ``__index``, while the
jbe@32 16 ``__len`` metamethod is never used.
jbe@32 17
jbe@0 18
jbe@0 19
jbe@0 20 Lua part of the library
jbe@0 21 -----------------------
jbe@0 22
jbe@30 23 The modified ``ipairs(seq)`` and the new ``string.concat(sep, seq)`` functions
jbe@30 24 accept either a table or a function as ``seq``. This is demonstrated in the
jbe@30 25 following examples:
jbe@0 26
jbe@0 27 require "seqlua"
jbe@0 28
jbe@0 29 t = {"a", "b", "c"}
jbe@0 30
jbe@0 31 for i, v in ipairs(t) do
jbe@0 32 print(i, v)
jbe@0 33 end
jbe@0 34 -- prints:
jbe@0 35 -- 1 a
jbe@0 36 -- 2 b
jbe@0 37 -- 3 c
jbe@0 38
jbe@25 39 print(string.concat(",", t))
jbe@25 40 -- prints: a,b,c
jbe@25 41
jbe@19 42 function alphabet()
jbe@0 43 local letter = nil
jbe@0 44 return function()
jbe@0 45 if letter == nil then
jbe@19 46 letter = "a"
jbe@19 47 elseif letter == "z" then
jbe@0 48 return nil
jbe@0 49 else
jbe@0 50 letter = string.char(string.byte(letter) + 1)
jbe@0 51 end
jbe@0 52 return letter
jbe@0 53 end
jbe@0 54 end
jbe@0 55
jbe@23 56 for i, v in ipairs(alphabet()) do
jbe@0 57 print(i, v)
jbe@0 58 end
jbe@0 59 -- prints:
jbe@0 60 -- 1 a
jbe@0 61 -- 2 b
jbe@0 62 -- 3 c
jbe@0 63 -- ...
jbe@0 64 -- 25 y
jbe@0 65 -- 26 z
jbe@0 66
jbe@25 67 print(string.concat(",", alphabet()))
jbe@25 68 -- prints: a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z
jbe@25 69
jbe@26 70 function filter(f)
jbe@26 71 return function(seq)
jbe@26 72 return coroutine.wrap(function()
jbe@26 73 for i, v in ipairs(seq) do f(v) end
jbe@26 74 end)
jbe@26 75 end
jbe@0 76 end
jbe@19 77
jbe@29 78 alpha_beta_x = filter(function(v)
jbe@28 79 if v == "a" then
jbe@28 80 coroutine.yield("alpha")
jbe@28 81 elseif v == "b" then
jbe@28 82 coroutine.yield("beta")
jbe@28 83 elseif type(v) == "number" then
jbe@23 84 for i = 1, v do
jbe@28 85 coroutine.yield("X")
jbe@23 86 end
jbe@0 87 end
jbe@26 88 end)
jbe@0 89
jbe@29 90 print((","):concat(alpha_beta_x{"a", 3, "b", "c", "d"}))
jbe@28 91 -- prints: alpha,X,X,X,beta
jbe@25 92
jbe@29 93 print((","):concat(alpha_beta_x(alphabet())))
jbe@28 94 -- prints: alpha,beta
jbe@27 95
jbe@0 96
jbe@0 97 C part of the library
jbe@0 98 ---------------------
jbe@0 99
jbe@0 100 In ``seqlualib.h``, the following macro is defined:
jbe@0 101
jbe@0 102 #define seqlua_iterloop(L, iter, idx) \
jbe@0 103 for ( \
jbe@0 104 seqlua_iterinit((L), (iter), (idx)); \
jbe@0 105 seqlua_iternext(iter); \
jbe@25 106 )
jbe@25 107
jbe@25 108 and
jbe@25 109
jbe@25 110 #define seqlua_iterloopauto(L, iter, idx) \
jbe@25 111 for ( \
jbe@25 112 seqlua_iterinit((L), (iter), (idx)); \
jbe@25 113 seqlua_iternext(iter); \
jbe@0 114 lua_pop((L), 1) \
jbe@0 115 )
jbe@0 116
jbe@23 117 This macro allows iteration over either tables or iterator functions as the
jbe@23 118 following example function demonstrates:
jbe@0 119
jbe@0 120 int printcsv(lua_State *L) {
jbe@0 121 seqlua_Iterator iter;
jbe@0 122 seqlua_iterloop(L, &iter, 1) {
jbe@0 123 if (seqlua_itercount(&iter) > 1) fputs(",", stdout);
jbe@0 124 fputs(luaL_tolstring(L, -1, NULL), stdout);
jbe@25 125 // two values need to be popped (the value pushed by
jbe@25 126 // seqlua_iternext and the value pushed by luaL_tolstring)
jbe@25 127 lua_pop(L, 2);
jbe@0 128 }
jbe@0 129 fputs("\n", stdout);
jbe@0 130 return 0;
jbe@0 131 }
jbe@0 132
jbe@11 133 printcsv{"a", "b", "c"}
jbe@11 134 -- prints: a,b,c
jbe@11 135
jbe@11 136 printcsv(assert(io.open("testfile")):lines())
jbe@11 137 -- prints: line1,line2,... of "testfile"
jbe@0 138
jbe@31 139 NOTE: During iteration using ``seqlua_iterloop``, ``seqlua_iterloopauto``, or
jbe@31 140 ``seqlua_iterinit``, three extra elements are stored on the stack (additionally
jbe@31 141 to the value). These extra elements are removed automatically when the loop ends
jbe@31 142 (i.e. when ``seqlua_iternext`` returns zero). The value pushed onto the stack
jbe@31 143 for every iteration step has to be removed manually from the stack, unless
jbe@31 144 ``seqlua_iterloopauto`` is used.
jbe@0 145
jbe@31 146
jbe@35 147 Respected metamethods
jbe@35 148 ---------------------
jbe@35 149
jbe@35 150 Regarding the behavior of the Lua functions and the C functions and macros
jbe@35 151 provided by this extension, an existing ``__index`` metamethod will be
jbe@35 152 respected automatically. An existing ``__ipairs`` metamethod, however, takes
jbe@35 153 precedence.
jbe@35 154
jbe@35 155 If the ``__ipairs`` field of a value's metatable is set, then it must always
jbe@35 156 refer to a function. When starting iteration over a value with such a
jbe@35 157 metamethod being set, then this function is called with ``self`` (i.e. the
jbe@35 158 value itself) passed as first argument. The return values of the ``__ipairs``
jbe@35 159 metamethod may take one of the following 4 forms:
jbe@35 160
jbe@35 161 * ``return function_or_callable, static_argument, startindex`` causes the three
jbe@35 162 arguments to be returned by ``ipairs`` without further modification. Using
jbe@35 163 the C macros and functions for iteration, the behavior is according to the
jbe@35 164 generic loop statement in Lua:
jbe@35 165 ``for i, v in function_or_callable, static_argument, startindex do ... end``
jbe@35 166 * ``return "raw", table`` will result in iteration over the table ``table``
jbe@35 167 using ``lua_rawgeti``
jbe@35 168 * ``return "index", table_or_userdata`` will result in iteration over the table
jbe@35 169 or userdata while respecting any ``__index`` metamethod of the table or
jbe@35 170 userdata value
jbe@35 171 * ``return "call", function_or_callable`` will use the callable value as
jbe@35 172 (function) iterator where the function is expected to return a single value
jbe@35 173 without any index (the index is inserted automatically when using the
jbe@35 174 ``ipairs`` function for iteration)
jbe@35 175
jbe@35 176 These possiblities are demonstrated by the following example code:
jbe@35 177
jbe@35 178 require "seqlua"
jbe@35 179
jbe@35 180 do
jbe@35 181 local function ipairsaux(t, i)
jbe@35 182 i = i + 1
jbe@35 183 if i <= 3 then
jbe@35 184 return i, t[i]
jbe@35 185 end
jbe@35 186 end
jbe@35 187 custom = setmetatable(
jbe@35 188 {"one", "two", "three", "four", "five"},
jbe@35 189 {
jbe@35 190 __ipairs = function(self)
jbe@35 191 return ipairsaux, self, 0
jbe@35 192 end
jbe@35 193 }
jbe@35 194 )
jbe@35 195 end
jbe@35 196 print(string.concat(",", custom))
jbe@36 197 -- prints: one,two,three
jbe@35 198 -- (note that "four" and "five" are not printed)
jbe@35 199
jbe@35 200 tbl = {"alpha", "beta"}
jbe@35 201
jbe@35 202 proxy1 = setmetatable({}, {__index = tbl})
jbe@35 203 for i, v in ipairs(proxy1) do print(i, v) end
jbe@35 204 -- prints:
jbe@35 205 -- 1 alpha
jbe@35 206 -- 2 beta
jbe@35 207
jbe@35 208 proxy2 = setmetatable({}, {
jbe@35 209 __ipairs = function(self)
jbe@35 210 return "index", proxy1
jbe@35 211 end
jbe@35 212 })
jbe@35 213 for i, v in ipairs(proxy2) do print(i, v) end
jbe@35 214 -- prints:
jbe@35 215 -- 1 alpha
jbe@35 216 -- 2 beta
jbe@35 217 print(proxy2[1])
jbe@35 218 -- prints: nil
jbe@35 219
jbe@35 220 cursor = setmetatable({
jbe@35 221 "alice", "bob", "charlie", pos=1
jbe@35 222 }, {
jbe@35 223 __call = function(self)
jbe@35 224 local value = self[self.pos]
jbe@35 225 if value == nil then
jbe@35 226 self.pos = 1
jbe@35 227 else
jbe@35 228 self.pos = self.pos + 1
jbe@35 229 end
jbe@35 230 return value
jbe@35 231 end,
jbe@35 232 __ipairs = function(self)
jbe@35 233 return "call", self
jbe@35 234 end
jbe@35 235 })
jbe@35 236 for i, v in ipairs(cursor) do print(i, v) end
jbe@35 237 -- prints:
jbe@35 238 -- 1 alice
jbe@35 239 -- 2 bob
jbe@35 240 -- 3 charlie
jbe@35 241 print(cursor())
jbe@35 242 -- prints: alice
jbe@35 243 for i, v in ipairs(cursor) do print(i, v) end
jbe@35 244 -- prints:
jbe@35 245 -- 1 bob
jbe@35 246 -- 2 charlie
jbe@35 247 -- (note that "alice" has been returned earlier)
jbe@35 248
jbe@35 249 coefficients = setmetatable({1.25, 3.14, 17.5}, {
jbe@35 250 __index = function(self) return 1 end,
jbe@35 251 __ipairs = function(self) return "raw", self end
jbe@35 252 })
jbe@35 253 for i, v in ipairs(coefficients) do print(i, v) end
jbe@35 254 -- prints:
jbe@35 255 -- 1 1.25
jbe@35 256 -- 2 3.14
jbe@35 257 -- 3 17.5
jbe@35 258 -- (note that iteration terminates even if coefficients[4] == 1)
jbe@35 259 print(coefficients[4])
jbe@35 260 -- prints: 1
jbe@35 261
jbe@35 262

Impressum / About Us