Skip to content

Instantly share code, notes, and snippets.

@wingeng
Last active November 7, 2018 07:29
Show Gist options
  • Save wingeng/7118960 to your computer and use it in GitHub Desktop.
Save wingeng/7118960 to your computer and use it in GitHub Desktop.
Description of making a stateless iterator for Lua.
-- The section describing iterators was configusing to me. http://www.lua.org/pil/7.1.html
--
-- I think it simpler to describe the 'stateless' iterator first, then
-- go into the nitty details of a 'stateful' iterator using closures.
--
--
-- Example of making an iterator in lua
--
-- The 'real' syntax for the 'for' statement is this
-- for <v> in <iter-fn>, <thing_2_iterate>, <initial-key> do
-- ..
-- end
--
-- NOTE: that the part after 'in' is a tuple. The first element
-- being a function, the iterator, the second is the object
-- you wish to iterate over, and the third the initial key.
-- (If you leave out the <initial-key>, conveniently it
-- gets set to nil)
--
-- 'for' uses the iterator function like this.
-- it gets the 'first' key by calling
-- <iter-fn>(<thing_2_iterate>, <initial-key>)
--
-- Your iterator returns a tuple containing the
-- next key, and the value. (ie return 1, "first")
--
-- Then, on subseqent iterations it uses the
-- the key returned as input to the call to the iterator
--
-- <iter-fn>(<thing_2_iterate>, 1)
--
-- When you want iteration to stop, the iterator function
-- should return nil. (note, not a tuple, just nil)
--
-- Here's an example of an iterator that counts by 2 until
-- some max
--
function iter_by_2 (max_var, key)
if (key == nil) then
-- start, return next-key of 2, and
-- value that is the concatenation of
-- the string "value:" with an initial index of 2
return 2, "value: " .. 2
end
-- sentinel value
if (key >= max_var) then
return nil
else
-- next iteration by 2
return key + 2, "value: " .. key + 2
end
end
-- Use like this
for k, v in iter_by_2, 8, nil do
print(k, v)
end
--
-- you should see output like
--
-- > for k, v in iter_by_2, 8, nil do print(k, v) end
-- 2 value: 2
-- 4 value: 4
-- 6 value: 6
-- 8 value: 8
--
-- Note 'nil' can be dropped and the output is the same
--
-- > for k, v in iter_by_2, 8 do print(k, v) end
-- 2 value: 2
-- 4 value: 4
-- 6 value: 6
-- 8 value: 8
--
-- Most times in lua the tuple after the 'in' is a function
-- that wraps the iterator and the object to iterate
--
function by2 (max_val)
return iter_by_2, max_val
end
--
-- so that the call is like this
--
for k, v in by2(8) do
print(k, i)
end
--
-- Note that the assignment in the 'for' statement
-- simply assigns the tuples returned by iterator function
-- More generally, the iterator can return *multiple* values
-- that can be assigned.
--
-- In the iter_by_2 example, the iterator returned two things
-- a key, value. It can instead return n things that are
-- assigned in the for.
--
-- Here's an example that returns 3 things, get the trend?
--
function iter_by_2_3 (max_var, key)
if (key == nil) then
return 2, "value: " .. 2, "second value: " .. 2 * 3
end
-- sentinel value
if (key >= max_var) then
return nil
else
-- next iteration by 2
return key + 2, "value: " .. key + 2, "second value: " .. key * 3
end
end
for k, v1, v2 in iter_by_2_3, 8, nil do
print(k, v1, v2)
end
--
-- Note, the assignment in the 'for' statement
-- simply assigns the tuples returned by iterator function
-- More generally, the iterator can return *multiple* values
-- that can be assigned.
--
-- In the iter_by_2 example, the iterator returned two things
-- a key, value. It can instead return n things that are
-- assigned in the for.
--
-- Here's an example that returns 3 things, get the trend?
--
function iter_by_2_3 (max_var, key)
if (key == nil) then
return 2, "value: " .. 2, "second value: " .. 2 * 3
end
-- sentinel value
if (key >= max_var) then
return nil
else
-- next iteration by 2
return key + 2, "value: " .. key + 2, "second value: " .. key * 3
end
end
for k, v1, v2 in iter_by_2_3, 8, nil do
print(k, v1, v2)
end
-- Final note, at the beginning of this gist, I said that the for syntax is
-- for <v> in <iter-fn>, <thing_2_iterate>, <initial-key> do
--
-- This is a bit of a simplification.
--
-- The <object-2-iterate> is simply the invariant, not really an object.
-- In my example it was the 'max' we want to iterate to, not an object like
-- a table. The variant is the second input to your iterator function
--
-- So the real syntax is
-- for <key>, <v1> .. <vn> in <iter-fn>, <invariant>, <initial-key> do
-- print(key< <v1>, .. <vn>)
-- end
--
-- function my_iter_fn (my_invariant, key)
-- end
--
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment