|
On 05/02/15 06:35 PM, Sean Conner
wrote:
According to the manual: http://www.lua.org/manual/5.3/manual.html#pdf-string.gmatchSome background: At work, I'm in the process of writing some regression tests for our product. We have a text file describing all our test cases. It's a tab-delimeted file---each line describes a test, and each field on a line is a value to use. As an example: #id use-a use-b datum1 datum2 1.0001 false false polyphyletically pursue 1.0002 false true penchute dentata 1.0003 true false advectitious flinkite 1.0004 true true hansel galvanocontractility I would like to use a construct such as: for testcase in next_test_cases("master-list") do -- process test case -- here, just print out the data print( testcase.id, testcase.use_a, testcase.use_b, testcase.datum1, testcase.datum2 ) end So, I need an interator. Not a problem. But here is my question. According to the Lua documenation: A for statement like for var_1, ···, var_n in explist do block end is equivalent to the code: do local f, s, var = explist while true do local var_1, ···, var_n = f(s, var) if var_1 == nil then break end var = var_1 block end end Note the following: * explist is evaluated only once. Its results are an iterator function, a state, and an initial value for the first iterator variable. * f, s, and var are invisible variables. The names are here for explanatory purposes only. * You can use break to exit a for loop. * The loop variables var_i are local to the loop; you cannot use their values after the for ends. If you need these values, then assign them to other variables before breaking or exiting the loop. Okay, so I write my iterator: function next_test_case(cases) local nextline,state,line = io.lines(cases) return function() line = nextline(state,line) if not line then return nil end local nextfield,fs,fv = line:gmatch("[^\t]+") local res = {} res.id = nextfield(fs,fv) res.use_a = nextfield(fs,res.id) res.use_b = nextfield(fs,res.use_a) res.datum1 = nextfield(fs,res.use_b) res.datum2 = nextfield(fs,res.datum1) return res end end But in playing around, I notice that io.lines() and string.gmatch() actually only return a function; they do *not* return a state and an initial variable value. Assuming that, I can rewrite my iterator as: function next_test_case(cases) local nextline = io.lines(cases) return function() local line = nextline() if not line then return nil end local nextfield = line:gmatch("[^\t]+") return { id = nextfield(), use_a = nextfield(), use_b = nextfield(), datum1 = nextfield(), datum2 = nextfield() } end end It's (in my opinion) cleaner and easier to read. Now my question: can I rely upon this behavior of io.lines() and string.gmatch() to only return a single function? I tested it on Lua 5.1, 5.2, 5.3 and LuaJIT, and in each case, only a function was returned. Am I relying upon an implementation detail? Should I use the first version, where I pass in the state and value each time, to be "safe"? -spc "Returns an *iterator function*" I believe it is safe to assume "iterator function" means it returns only a function that returns the next match or nil at the end. (If it said just "an iterator", THEN it wouldn't be safe to assume so) Same goes for io.lines and a few others: http://www.lua.org/manual/5.3/manual.html#pdf-io.lines http://www.lua.org/manual/5.3/manual.html#pdf-file:lines See also: http://www.lua.org/manual/5.3/manual.html#3.3.5 "The generic for statement works over functions, called iterators. On each iteration, the iterator function is called to produce a new value, stopping when this new value is nil." [...] " explist
is evaluated only once.
Its results are an *iterator function*,
a state,
and an initial value for the first iterator variable."The manual for ipairs also talks about an iterator function: http://www.lua.org/manual/5.3/manual.html#pdf-ipairs "Returns three values (an *iterator function*, the table t ,
and 0)"-- Disclaimer: these emails are public and can be accessed from <TODO: get a non-DHCP IP and put it here>. If you do not agree with this, DO NOT REPLY. |