| rev | 
   line source | 
| 
jbe@37
 | 
     1 seqlua: Extension for handling sequential data in Lua
 | 
| 
jbe@37
 | 
     2 =====================================================
 | 
| 
jbe@0
 | 
     3 
 | 
| 
jbe@55
 | 
     4 This package is an experimental extension for the Lua 5.2 programming language
 | 
| 
jbe@54
 | 
     5 which:
 | 
| 
jbe@0
 | 
     6 
 | 
| 
jbe@54
 | 
     7 * allows ``ipairs(seq)`` to accept either tables or functions (i.e function
 | 
| 
jbe@54
 | 
     8   iterators) as an argument,
 | 
| 
jbe@49
 | 
     9 * adds a new function ``string.concat(separator, seq)`` that concats either
 | 
| 
jbe@32
 | 
    10   table entries or function return values,
 | 
| 
jbe@49
 | 
    11 * provides auxiliary C functions and macros to simplify iterating over both
 | 
| 
jbe@38
 | 
    12   tables and iterator functions with a generic statement.
 | 
| 
jbe@0
 | 
    13 
 | 
| 
jbe@33
 | 
    14 Existing ``__ipairs`` or ``__index`` (but not ``__len``) metamethods are
 | 
| 
jbe@33
 | 
    15 respected by both the Lua functions and the C functions and macros. The
 | 
| 
jbe@32
 | 
    16 ``__ipairs`` metamethod takes precedence over ``__index``, while the
 | 
| 
jbe@32
 | 
    17 ``__len`` metamethod is never used.
 | 
| 
jbe@32
 | 
    18 
 | 
| 
jbe@37
 | 
    19 Metamethod handling in detail is explained in the last section
 | 
| 
jbe@37
 | 
    20 ("Respected metamethods") at the bottom of this README.
 | 
| 
jbe@37
 | 
    21 
 | 
| 
jbe@49
 | 
    22 In Lua, this extension is loaded by ``require "seqlua"``. In order to use the
 | 
| 
jbe@49
 | 
    23 auxiliary C functions and macros, add ``#include <seqlualib.h>`` to your C file
 | 
| 
jbe@49
 | 
    24 and ensure that the functions implemented in ``seqlualib.c`` are statically or
 | 
| 
jbe@49
 | 
    25 dynamically linked with your C Lua library.
 | 
| 
jbe@49
 | 
    26 
 | 
| 
jbe@37
 | 
    27 
 | 
| 
jbe@37
 | 
    28 
 | 
| 
jbe@37
 | 
    29 Motivation
 | 
| 
jbe@37
 | 
    30 ----------
 | 
| 
jbe@37
 | 
    31 
 | 
| 
jbe@37
 | 
    32 Sequential data (such as arrays or streams) is often represented in two
 | 
| 
jbe@37
 | 
    33 different ways:
 | 
| 
jbe@37
 | 
    34 
 | 
| 
jbe@37
 | 
    35 * as an ordered set of values (usually implemented as an array in other
 | 
| 
jbe@37
 | 
    36   programming languages, or as a sequence in Lua: a table with numeric keys
 | 
| 
jbe@37
 | 
    37   {1..n} associated with a value each),
 | 
| 
jbe@37
 | 
    38 * as some sort of data stream (sometimes implemented as a class of objects
 | 
| 
jbe@37
 | 
    39   providing certain methods, or as an iterator function in Lua: a function that
 | 
| 
jbe@37
 | 
    40   returns the next value with every call, where nil indicates the end of the
 | 
| 
jbe@37
 | 
    41   stream).
 | 
| 
jbe@37
 | 
    42 
 | 
| 
jbe@37
 | 
    43 Quite often, when functions work on sequential data, it shouldn't matter in
 | 
| 
jbe@37
 | 
    44 which form the sequential data is being provided to the function. As an
 | 
| 
jbe@37
 | 
    45 example, consider a function that is writing a sequence of strings to a file.
 | 
| 
jbe@37
 | 
    46 Such function could either be fed with an array of strings (a table with
 | 
| 
jbe@37
 | 
    47 numeric keys in Lua) or with a (possibly infinite) stream of data (an iterator
 | 
| 
jbe@37
 | 
    48 function in Lua).
 | 
| 
jbe@37
 | 
    49 
 | 
| 
jbe@37
 | 
    50 A function in Lua that accepts a table, might look like as follows:
 | 
| 
jbe@37
 | 
    51 
 | 
| 
jbe@37
 | 
    52     function write_lines(lines)
 | 
| 
jbe@37
 | 
    53       for i, line in ipairs(lines) do
 | 
| 
jbe@37
 | 
    54         io.stdout:write(line)
 | 
| 
jbe@37
 | 
    55         io.stdout:write("\n")
 | 
| 
jbe@37
 | 
    56       end
 | 
| 
jbe@37
 | 
    57     end
 | 
| 
jbe@37
 | 
    58 
 | 
| 
jbe@37
 | 
    59 In contrast, a function in Lua that accepts an iterator function would have to
 | 
| 
jbe@37
 | 
    60 be implemented differently:
 | 
| 
jbe@37
 | 
    61 
 | 
| 
jbe@37
 | 
    62     function write_lines(get_next_line)
 | 
| 
jbe@37
 | 
    63       for line in get_next_line do
 | 
| 
jbe@37
 | 
    64         io.stdout:write(line)
 | 
| 
jbe@37
 | 
    65         io.stdout:write("\n")
 | 
| 
jbe@37
 | 
    66       end
 | 
| 
jbe@37
 | 
    67     end
 | 
| 
jbe@37
 | 
    68 
 | 
| 
jbe@37
 | 
    69 If one wanted to create a function that accepts either a sequence in form of a
 | 
| 
jbe@37
 | 
    70 table or an iterator function, then one might need to write:
 | 
| 
jbe@37
 | 
    71 
 | 
| 
jbe@41
 | 
    72     do
 | 
| 
jbe@41
 | 
    73       local function write_line(line)
 | 
| 
jbe@37
 | 
    74         io.stdout:write(line)
 | 
| 
jbe@37
 | 
    75         io.stdout:write("\n")
 | 
| 
jbe@37
 | 
    76       end
 | 
| 
jbe@41
 | 
    77       function write_lines(lines)
 | 
| 
jbe@41
 | 
    78         if type(lines) == "function" then
 | 
| 
jbe@41
 | 
    79           for line in lines do
 | 
| 
jbe@41
 | 
    80             write_line(line)
 | 
| 
jbe@41
 | 
    81           end
 | 
| 
jbe@41
 | 
    82         else
 | 
| 
jbe@41
 | 
    83           for i, line in ipairs(lines) do
 | 
| 
jbe@41
 | 
    84             write_line(line)
 | 
| 
jbe@41
 | 
    85           end
 | 
| 
jbe@41
 | 
    86         end
 | 
| 
jbe@41
 | 
    87       end
 | 
| 
jbe@37
 | 
    88     end
 | 
| 
jbe@37
 | 
    89 
 | 
| 
jbe@41
 | 
    90 Obviously, this isn't something we want to do in every function that accepts
 | 
| 
jbe@37
 | 
    91 sequential data. Therefore, we usually decide for one of the two first forms
 | 
| 
jbe@48
 | 
    92 and thus disallow the other possible representation of sequential data to be
 | 
| 
jbe@48
 | 
    93 passed to the function.
 | 
| 
jbe@37
 | 
    94 
 | 
| 
jbe@37
 | 
    95 This extension, however, modifies Lua's ``ipairs`` statement in such way that
 | 
| 
jbe@37
 | 
    96 it automatically accepts either a table or an iterator function as argument.
 | 
| 
jbe@54
 | 
    97 Thus, the first of the three ``write_lines`` functions above will accept both
 | 
| 
jbe@54
 | 
    98 (table) sequences and (function) iterators.
 | 
| 
jbe@37
 | 
    99 
 | 
| 
jbe@37
 | 
   100 In addition to the modification of ``ipairs``, it also provides C functions and
 | 
| 
jbe@37
 | 
   101 macros to iterate over values in the same manner as a generic loop statement
 | 
| 
jbe@37
 | 
   102 with ``ipairs`` would do.
 | 
| 
jbe@37
 | 
   103 
 | 
| 
jbe@56
 | 
   104 This extension doesn't aim to supersede Lua's concept of iterator functions.
 | 
| 
jbe@56
 | 
   105 While metamethods (see section "Respected metamethods" below) may be used to
 | 
| 
jbe@56
 | 
   106 customize iteration behavior on values, this extension isn't thought to replace
 | 
| 
jbe@56
 | 
   107 the common practice to use function closures as iterators. Consider the
 | 
| 
jbe@56
 | 
   108 following example:
 | 
| 
jbe@56
 | 
   109 
 | 
| 
jbe@56
 | 
   110     function write_lines(lines)
 | 
| 
jbe@56
 | 
   111       for i, line in ipairs(lines) do
 | 
| 
jbe@56
 | 
   112         io.stdout:write(line)
 | 
| 
jbe@56
 | 
   113         io.stdout:write("\n")
 | 
| 
jbe@56
 | 
   114       end
 | 
| 
jbe@56
 | 
   115     end
 | 
| 
jbe@56
 | 
   116     local result = sql_query("SELECT * FROM actor ORDER BY birthdate")
 | 
| 
jbe@56
 | 
   117     -- assert(type(result:get_column_entries("name")) == "function")
 | 
| 
jbe@56
 | 
   118     write_lines(result:get_column_entries("name"))
 | 
| 
jbe@56
 | 
   119 
 | 
| 
jbe@56
 | 
   120 Note, however, that in case of repeated or nested loops, using function
 | 
| 
jbe@56
 | 
   121 iterators may not be feasible:
 | 
| 
jbe@55
 | 
   122 
 | 
| 
jbe@55
 | 
   123     function print_list_twice(seq)
 | 
| 
jbe@55
 | 
   124       for i = 1, 2 do
 | 
| 
jbe@55
 | 
   125         for i, v in ipairs(seq) do
 | 
| 
jbe@55
 | 
   126           print(v)
 | 
| 
jbe@55
 | 
   127         end
 | 
| 
jbe@55
 | 
   128       end
 | 
| 
jbe@55
 | 
   129     end
 | 
| 
jbe@55
 | 
   130     print_list_twice(io.stdin:lines())  -- won't work as expected
 | 
| 
jbe@55
 | 
   131 
 | 
| 
jbe@56
 | 
   132 Where desired, it is possible to use metamethods to customize iteration
 | 
| 
jbe@44
 | 
   133 behavior:
 | 
| 
jbe@44
 | 
   134 
 | 
| 
jbe@44
 | 
   135     function print_rows(rows)
 | 
| 
jbe@44
 | 
   136       for i, row in ipairs(rows) do
 | 
| 
jbe@44
 | 
   137         print_row(row)
 | 
| 
jbe@44
 | 
   138       end
 | 
| 
jbe@44
 | 
   139     end
 | 
| 
jbe@44
 | 
   140     local result = sql_query("SELECT * FROM actor ORDER BY birthday")
 | 
| 
jbe@46
 | 
   141     assert(type(result) == "userdata")
 | 
| 
jbe@44
 | 
   142 
 | 
| 
jbe@44
 | 
   143     -- we may rely on the ``__index`` or ``__ipairs`` metamethod to
 | 
| 
jbe@44
 | 
   144     -- iterate through all result rows here:
 | 
| 
jbe@44
 | 
   145     print_rows(result)  -- no need to use ":rows()" or a similar syntax
 | 
| 
jbe@44
 | 
   146 
 | 
| 
jbe@45
 | 
   147     -- but we can also still pass an individual set of result rows to the
 | 
| 
jbe@44
 | 
   148     -- print_rows function:
 | 
| 
jbe@44
 | 
   149     print_rows{result[1], result[#result]}
 | 
| 
jbe@44
 | 
   150 
 | 
| 
jbe@44
 | 
   151 This extension, however, doesn't respect the ``__len`` metamethod due to the
 | 
| 
jbe@47
 | 
   152 following considerations:
 | 
| 
jbe@37
 | 
   153 
 | 
| 
jbe@39
 | 
   154 * An efficient implementation where ``for i, v in ipairs(tbl) do ... end`` does
 | 
| 
jbe@39
 | 
   155   neither create a closure nor repeatedly evaluate ``#tbl`` seems to be
 | 
| 
jbe@39
 | 
   156   impossible.
 | 
| 
jbe@37
 | 
   157 * Respecting ``__len`` could be used to implement sparse arrays, but this would
 | 
| 
jbe@37
 | 
   158   require iterating functions to expect ``nil`` as a potential value. This may
 | 
| 
jbe@37
 | 
   159   lead to problems because ``nil`` is usually also used to indicate the absence
 | 
| 
jbe@37
 | 
   160   of a value.
 | 
| 
jbe@37
 | 
   161 
 | 
| 
jbe@40
 | 
   162 Though, if such behavior is desired, it can still be implemented through the
 | 
| 
jbe@37
 | 
   163 ``__ipairs`` metamethod.
 | 
| 
jbe@37
 | 
   164 
 | 
| 
jbe@48
 | 
   165 Unless manually done by the user in the ``__ipairs`` metamethod, the ``ipairs``
 | 
| 
jbe@48
 | 
   166 function as well as the corresponding C functions and macros provided by this
 | 
| 
jbe@48
 | 
   167 extension never create any closures or other values that need to be garbage
 | 
| 
jbe@48
 | 
   168 collected.
 | 
| 
jbe@37
 | 
   169 
 | 
| 
jbe@0
 | 
   170 
 | 
| 
jbe@0
 | 
   171 
 | 
| 
jbe@0
 | 
   172 Lua part of the library
 | 
| 
jbe@0
 | 
   173 -----------------------
 | 
| 
jbe@0
 | 
   174 
 | 
| 
jbe@30
 | 
   175 The modified ``ipairs(seq)`` and the new ``string.concat(sep, seq)`` functions
 | 
| 
jbe@30
 | 
   176 accept either a table or a function as ``seq``. This is demonstrated in the
 | 
| 
jbe@30
 | 
   177 following examples:
 | 
| 
jbe@0
 | 
   178 
 | 
| 
jbe@0
 | 
   179     require "seqlua"
 | 
| 
jbe@0
 | 
   180 
 | 
| 
jbe@0
 | 
   181     t = {"a", "b", "c"}
 | 
| 
jbe@0
 | 
   182 
 | 
| 
jbe@54
 | 
   183     for i, v in ipairs(t) do
 | 
| 
jbe@0
 | 
   184       print(i, v)
 | 
| 
jbe@0
 | 
   185     end
 | 
| 
jbe@0
 | 
   186     -- prints:
 | 
| 
jbe@0
 | 
   187     --  1   a
 | 
| 
jbe@0
 | 
   188     --  2   b
 | 
| 
jbe@0
 | 
   189     --  3   c
 | 
| 
jbe@0
 | 
   190 
 | 
| 
jbe@25
 | 
   191     print(string.concat(",", t))
 | 
| 
jbe@25
 | 
   192     -- prints: a,b,c
 | 
| 
jbe@25
 | 
   193 
 | 
| 
jbe@19
 | 
   194     function alphabet()
 | 
| 
jbe@0
 | 
   195       local letter = nil
 | 
| 
jbe@0
 | 
   196       return function()
 | 
| 
jbe@0
 | 
   197         if letter == nil then
 | 
| 
jbe@19
 | 
   198           letter = "a"
 | 
| 
jbe@19
 | 
   199         elseif letter == "z" then
 | 
| 
jbe@0
 | 
   200           return nil
 | 
| 
jbe@0
 | 
   201         else
 | 
| 
jbe@0
 | 
   202           letter = string.char(string.byte(letter) + 1)
 | 
| 
jbe@0
 | 
   203         end
 | 
| 
jbe@0
 | 
   204         return letter
 | 
| 
jbe@0
 | 
   205       end
 | 
| 
jbe@0
 | 
   206     end
 | 
| 
jbe@0
 | 
   207 
 | 
| 
jbe@54
 | 
   208     for i, v in ipairs(alphabet()) do
 | 
| 
jbe@0
 | 
   209       print(i, v)
 | 
| 
jbe@0
 | 
   210     end
 | 
| 
jbe@0
 | 
   211     -- prints:
 | 
| 
jbe@0
 | 
   212     --  1   a
 | 
| 
jbe@0
 | 
   213     --  2   b
 | 
| 
jbe@0
 | 
   214     --  3   c
 | 
| 
jbe@0
 | 
   215     --  ...
 | 
| 
jbe@0
 | 
   216     --  25  y
 | 
| 
jbe@0
 | 
   217     --  26  z
 | 
| 
jbe@0
 | 
   218 
 | 
| 
jbe@25
 | 
   219     print(string.concat(",", alphabet()))
 | 
| 
jbe@25
 | 
   220     -- 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
 | 
   221 
 | 
| 
jbe@26
 | 
   222     function filter(f)
 | 
| 
jbe@26
 | 
   223       return function(seq)
 | 
| 
jbe@26
 | 
   224         return coroutine.wrap(function()
 | 
| 
jbe@54
 | 
   225           for i, v in ipairs(seq) do f(v) end
 | 
| 
jbe@26
 | 
   226         end)
 | 
| 
jbe@26
 | 
   227       end
 | 
| 
jbe@0
 | 
   228     end
 | 
| 
jbe@19
 | 
   229 
 | 
| 
jbe@29
 | 
   230     alpha_beta_x = filter(function(v)
 | 
| 
jbe@28
 | 
   231       if v == "a" then
 | 
| 
jbe@28
 | 
   232         coroutine.yield("alpha")
 | 
| 
jbe@28
 | 
   233       elseif v == "b" then
 | 
| 
jbe@28
 | 
   234         coroutine.yield("beta")
 | 
| 
jbe@28
 | 
   235       elseif type(v) == "number" then
 | 
| 
jbe@23
 | 
   236         for i = 1, v do
 | 
| 
jbe@28
 | 
   237           coroutine.yield("X")
 | 
| 
jbe@23
 | 
   238         end
 | 
| 
jbe@0
 | 
   239       end
 | 
| 
jbe@26
 | 
   240     end)
 | 
| 
jbe@0
 | 
   241 
 | 
| 
jbe@29
 | 
   242     print((","):concat(alpha_beta_x{"a", 3, "b", "c", "d"}))
 | 
| 
jbe@28
 | 
   243     -- prints: alpha,X,X,X,beta
 | 
| 
jbe@25
 | 
   244 
 | 
| 
jbe@29
 | 
   245     print((","):concat(alpha_beta_x(alphabet())))
 | 
| 
jbe@28
 | 
   246     -- prints: alpha,beta
 | 
| 
jbe@27
 | 
   247 
 | 
| 
jbe@0
 | 
   248 
 | 
| 
jbe@37
 | 
   249 
 | 
| 
jbe@0
 | 
   250 C part of the library
 | 
| 
jbe@0
 | 
   251 ---------------------
 | 
| 
jbe@0
 | 
   252 
 | 
| 
jbe@0
 | 
   253 In ``seqlualib.h``, the following macro is defined:
 | 
| 
jbe@0
 | 
   254 
 | 
| 
jbe@54
 | 
   255     #define seqlua_iterloop(L, iter, idx) \
 | 
| 
jbe@0
 | 
   256       for ( \
 | 
| 
jbe@54
 | 
   257         seqlua_iterinit((L), (iter), (idx)); \
 | 
| 
jbe@0
 | 
   258         seqlua_iternext(iter); \
 | 
| 
jbe@25
 | 
   259       )
 | 
| 
jbe@25
 | 
   260 
 | 
| 
jbe@25
 | 
   261 and
 | 
| 
jbe@25
 | 
   262 
 | 
| 
jbe@25
 | 
   263     #define seqlua_iterloopauto(L, iter, idx) \
 | 
| 
jbe@25
 | 
   264       for ( \
 | 
| 
jbe@54
 | 
   265         seqlua_iterinit((L), (iter), (idx)); \
 | 
| 
jbe@25
 | 
   266         seqlua_iternext(iter); \
 | 
| 
jbe@0
 | 
   267         lua_pop((L), 1) \
 | 
| 
jbe@0
 | 
   268       )
 | 
| 
jbe@0
 | 
   269 
 | 
| 
jbe@23
 | 
   270 This macro allows iteration over either tables or iterator functions as the
 | 
| 
jbe@23
 | 
   271 following example function demonstrates:
 | 
| 
jbe@0
 | 
   272 
 | 
| 
jbe@0
 | 
   273     int printcsv(lua_State *L) {
 | 
| 
jbe@0
 | 
   274       seqlua_Iterator iter;
 | 
| 
jbe@54
 | 
   275       seqlua_iterloop(L, &iter, 1) {
 | 
| 
jbe@0
 | 
   276         if (seqlua_itercount(&iter) > 1) fputs(",", stdout);
 | 
| 
jbe@0
 | 
   277         fputs(luaL_tolstring(L, -1, NULL), stdout);
 | 
| 
jbe@25
 | 
   278         // two values need to be popped (the value pushed by
 | 
| 
jbe@25
 | 
   279         // seqlua_iternext and the value pushed by luaL_tolstring)
 | 
| 
jbe@25
 | 
   280         lua_pop(L, 2);
 | 
| 
jbe@0
 | 
   281       }
 | 
| 
jbe@0
 | 
   282       fputs("\n", stdout);
 | 
| 
jbe@0
 | 
   283       return 0;
 | 
| 
jbe@0
 | 
   284     }
 | 
| 
jbe@0
 | 
   285 
 | 
| 
jbe@11
 | 
   286     printcsv{"a", "b", "c"}
 | 
| 
jbe@11
 | 
   287     -- prints: a,b,c
 | 
| 
jbe@11
 | 
   288 
 | 
| 
jbe@11
 | 
   289     printcsv(assert(io.open("testfile")):lines())
 | 
| 
jbe@11
 | 
   290     -- prints: line1,line2,... of "testfile"
 | 
| 
jbe@0
 | 
   291 
 | 
| 
jbe@31
 | 
   292 NOTE: During iteration using ``seqlua_iterloop``, ``seqlua_iterloopauto``, or
 | 
| 
jbe@31
 | 
   293 ``seqlua_iterinit``, three extra elements are stored on the stack (additionally
 | 
| 
jbe@31
 | 
   294 to the value). These extra elements are removed automatically when the loop ends
 | 
| 
jbe@31
 | 
   295 (i.e. when ``seqlua_iternext`` returns zero). The value pushed onto the stack
 | 
| 
jbe@31
 | 
   296 for every iteration step has to be removed manually from the stack, unless
 | 
| 
jbe@31
 | 
   297 ``seqlua_iterloopauto`` is used.
 | 
| 
jbe@0
 | 
   298 
 | 
| 
jbe@31
 | 
   299 
 | 
| 
jbe@37
 | 
   300 
 | 
| 
jbe@35
 | 
   301 Respected metamethods
 | 
| 
jbe@35
 | 
   302 ---------------------
 | 
| 
jbe@35
 | 
   303 
 | 
| 
jbe@35
 | 
   304 Regarding the behavior of the Lua functions and the C functions and macros
 | 
| 
jbe@35
 | 
   305 provided by this extension, an existing ``__index`` metamethod will be
 | 
| 
jbe@35
 | 
   306 respected automatically. An existing ``__ipairs`` metamethod, however, takes
 | 
| 
jbe@35
 | 
   307 precedence.
 | 
| 
jbe@35
 | 
   308 
 | 
| 
jbe@35
 | 
   309 If the ``__ipairs`` field of a value's metatable is set, then it must always
 | 
| 
jbe@35
 | 
   310 refer to a function. When starting iteration over a value with such a
 | 
| 
jbe@35
 | 
   311 metamethod being set, then this function is called with ``self`` (i.e. the
 | 
| 
jbe@35
 | 
   312 value itself) passed as first argument. The return values of the ``__ipairs``
 | 
| 
jbe@35
 | 
   313 metamethod may take one of the following 4 forms:
 | 
| 
jbe@35
 | 
   314 
 | 
| 
jbe@35
 | 
   315 * ``return function_or_callable, static_argument, startindex`` causes the three
 | 
| 
jbe@35
 | 
   316   arguments to be returned by ``ipairs`` without further modification. Using
 | 
| 
jbe@35
 | 
   317   the C macros and functions for iteration, the behavior is according to the
 | 
| 
jbe@35
 | 
   318   generic loop statement in Lua:
 | 
| 
jbe@35
 | 
   319   ``for i, v in function_or_callable, static_argument, startindex do ... end``
 | 
| 
jbe@35
 | 
   320 * ``return "raw", table`` will result in iteration over the table ``table``
 | 
| 
jbe@35
 | 
   321   using ``lua_rawgeti``
 | 
| 
jbe@35
 | 
   322 * ``return "index", table_or_userdata`` will result in iteration over the table
 | 
| 
jbe@35
 | 
   323   or userdata while respecting any ``__index`` metamethod of the table or
 | 
| 
jbe@35
 | 
   324   userdata value
 | 
| 
jbe@35
 | 
   325 * ``return "call", function_or_callable`` will use the callable value as
 | 
| 
jbe@35
 | 
   326   (function) iterator where the function is expected to return a single value
 | 
| 
jbe@35
 | 
   327   without any index (the index is inserted automatically when using the
 | 
| 
jbe@35
 | 
   328   ``ipairs`` function for iteration)
 | 
| 
jbe@35
 | 
   329 
 | 
| 
jbe@35
 | 
   330 These possiblities are demonstrated by the following example code:
 | 
| 
jbe@35
 | 
   331 
 | 
| 
jbe@35
 | 
   332     require "seqlua"
 | 
| 
jbe@35
 | 
   333 
 | 
| 
jbe@35
 | 
   334     do
 | 
| 
jbe@35
 | 
   335       local function ipairsaux(t, i)
 | 
| 
jbe@35
 | 
   336         i = i + 1
 | 
| 
jbe@35
 | 
   337         if i <= 3 then
 | 
| 
jbe@35
 | 
   338           return i, t[i]
 | 
| 
jbe@35
 | 
   339         end
 | 
| 
jbe@35
 | 
   340       end
 | 
| 
jbe@35
 | 
   341       custom = setmetatable(
 | 
| 
jbe@35
 | 
   342         {"one", "two", "three", "four", "five"},
 | 
| 
jbe@35
 | 
   343         {
 | 
| 
jbe@35
 | 
   344           __ipairs = function(self)
 | 
| 
jbe@35
 | 
   345             return ipairsaux, self, 0
 | 
| 
jbe@35
 | 
   346           end
 | 
| 
jbe@35
 | 
   347         }
 | 
| 
jbe@35
 | 
   348       )
 | 
| 
jbe@35
 | 
   349     end
 | 
| 
jbe@35
 | 
   350     print(string.concat(",", custom))
 | 
| 
jbe@36
 | 
   351     -- prints: one,two,three
 | 
| 
jbe@35
 | 
   352     -- (note that "four" and "five" are not printed)
 | 
| 
jbe@35
 | 
   353 
 | 
| 
jbe@35
 | 
   354     tbl = {"alpha", "beta"}
 | 
| 
jbe@35
 | 
   355 
 | 
| 
jbe@35
 | 
   356     proxy1 = setmetatable({}, {__index = tbl})
 | 
| 
jbe@35
 | 
   357     for i, v in ipairs(proxy1) do print(i, v) end
 | 
| 
jbe@35
 | 
   358     -- prints:
 | 
| 
jbe@35
 | 
   359     --  1   alpha
 | 
| 
jbe@35
 | 
   360     --  2   beta
 | 
| 
jbe@35
 | 
   361 
 | 
| 
jbe@35
 | 
   362     proxy2 = setmetatable({}, {
 | 
| 
jbe@35
 | 
   363       __ipairs = function(self)
 | 
| 
jbe@35
 | 
   364         return "index", proxy1
 | 
| 
jbe@35
 | 
   365       end
 | 
| 
jbe@35
 | 
   366     })
 | 
| 
jbe@35
 | 
   367     for i, v in ipairs(proxy2) do print(i, v) end
 | 
| 
jbe@35
 | 
   368     -- prints:
 | 
| 
jbe@35
 | 
   369     --  1   alpha
 | 
| 
jbe@35
 | 
   370     --  2   beta
 | 
| 
jbe@35
 | 
   371     print(proxy2[1])
 | 
| 
jbe@35
 | 
   372     -- prints: nil
 | 
| 
jbe@35
 | 
   373 
 | 
| 
jbe@35
 | 
   374     cursor = setmetatable({
 | 
| 
jbe@35
 | 
   375       "alice", "bob", "charlie", pos=1
 | 
| 
jbe@35
 | 
   376     }, {
 | 
| 
jbe@35
 | 
   377       __call = function(self)
 | 
| 
jbe@35
 | 
   378         local value = self[self.pos]
 | 
| 
jbe@35
 | 
   379         if value == nil then
 | 
| 
jbe@35
 | 
   380           self.pos = 1
 | 
| 
jbe@35
 | 
   381         else
 | 
| 
jbe@35
 | 
   382           self.pos = self.pos + 1
 | 
| 
jbe@35
 | 
   383         end
 | 
| 
jbe@35
 | 
   384         return value
 | 
| 
jbe@35
 | 
   385       end,
 | 
| 
jbe@35
 | 
   386       __ipairs = function(self)
 | 
| 
jbe@35
 | 
   387         return "call", self
 | 
| 
jbe@35
 | 
   388       end
 | 
| 
jbe@35
 | 
   389     })
 | 
| 
jbe@35
 | 
   390     for i, v in ipairs(cursor) do print(i, v) end
 | 
| 
jbe@35
 | 
   391     -- prints:
 | 
| 
jbe@35
 | 
   392     --  1   alice
 | 
| 
jbe@35
 | 
   393     --  2   bob
 | 
| 
jbe@35
 | 
   394     --  3   charlie
 | 
| 
jbe@35
 | 
   395     print(cursor())
 | 
| 
jbe@35
 | 
   396     -- prints: alice
 | 
| 
jbe@35
 | 
   397     for i, v in ipairs(cursor) do print(i, v) end
 | 
| 
jbe@35
 | 
   398     -- prints:
 | 
| 
jbe@35
 | 
   399     --  1   bob
 | 
| 
jbe@35
 | 
   400     --  2   charlie
 | 
| 
jbe@35
 | 
   401     -- (note that "alice" has been returned earlier)
 | 
| 
jbe@35
 | 
   402 
 | 
| 
jbe@35
 | 
   403     coefficients = setmetatable({1.25, 3.14, 17.5}, {
 | 
| 
jbe@35
 | 
   404       __index  = function(self) return 1 end,
 | 
| 
jbe@35
 | 
   405       __ipairs = function(self) return "raw", self end
 | 
| 
jbe@35
 | 
   406     })
 | 
| 
jbe@35
 | 
   407     for i, v in ipairs(coefficients) do print(i, v) end
 | 
| 
jbe@35
 | 
   408     -- prints:
 | 
| 
jbe@35
 | 
   409     --  1   1.25
 | 
| 
jbe@35
 | 
   410     --  2   3.14
 | 
| 
jbe@35
 | 
   411     --  3   17.5
 | 
| 
jbe@35
 | 
   412     -- (note that iteration terminates even if coefficients[4] == 1)
 | 
| 
jbe@35
 | 
   413     print(coefficients[4])
 | 
| 
jbe@35
 | 
   414     -- prints: 1
 | 
| 
jbe@35
 | 
   415 
 | 
| 
jbe@35
 | 
   416 
 |