Last active
November 7, 2018 07:29
-
-
Save wingeng/7118960 to your computer and use it in GitHub Desktop.
Description of making a stateless iterator for Lua.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-- 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