Sleepy Coder

lua-users home
wiki

Hoy. :-) I am Sleepy_Coder in #lua on Freenode. I frequently talk/blabber and stress everyone out with the little things I nitpick at.

Toodles ~

Lua 5.3 Wishlist

For Loops Should Allow Modification of Iterator State Variables

-- Benefits of what I'm proposing:

-- No need for a 'continue' or 'skip' keyword if you can control the iterator from inside the loop body (it could even allow for break-like behavior)
-- With this it would be possible to skip several iterations of a loop, rather than just one-by-one if a skip or continue keyword were added

-- Problem

-- Lua's for loops don't let us modify the key and value (and whatever other identifier declared) passed
-- to the loop body when (on the next iteration) it uses these values for the next call of the iterator

    for k, v in ipairs({ 1, 2, 3, 4, 5 }) do 
        print(k, v) 

        if k == 2 then k = k + 1 end -- we can't actually do this to "skip" the 3rd iteration, but I wish we could
    end

-- ipairs({ 1, 2, 3, 4, 5 }) returns: iterator_function, { 1, 2, 3, 4, 5 }, 0, nil
-- 1st iteration: iterator_function({ 1, 2, 3, 4, 5 }, 0, nil) -- returns 1, 1
-- 2nd iteration: iterator_function({ 1, 2, 3, 4, 5 }, 1, 1)   -- returns 2, 2
-- 3rd iteration: etc...

-- What I Want:

    for k, v, not_used1 in ipairs({ 1, 2, 3, 4, 5 }) do 
        print(k, v, not_used1)

        -- we want to modify the first value passed to the next iteration
        if k == 2 then k = k + 1 end
    end

-- Translation:

    ret = { ipairs({ 1, 2, 3, 4, 5 }) }

    -- first 2 items from the generator (my_pairs()) would be the iterator and item it's iterating over
    -- (some generators don't have items they iterate over and return nil as the 2nd arg)
    iter, iter_self = unpack(ret)

    -- remove iter and iter_self from beginning
    ret = { unpack(ret, 3) } -- key value is at index 3

    -- create our initial k, v, not_used1
    k, v, not_used1 = unpack(ret)

    -- the rule is that iterators stop
    -- iterating when the k section is nil
    while true do     
        -- the table is needed because the iterator could return multiple items
        ret = { iter(iter_self, unpack(ret)) } -- our initial k, v, not_used1 are already in ret
        -- ^ this should be easier done in C when you can work with the stack, no new table on each iteration

        -- set our new key-value for inside the loop body
        k, v, not_used1 = unpack(ret)

        if k == nil then
            break
        end

        -- example loop body from above
        do 
            print(k, v, not_used1)

            -- we can now modify the first 2 values returned of the iterator to control the loop behavior
            if k == 2 then k = k + 1 end 
        end

        -- these may have changed, reset ret
        ret = { k, v, not_used1, unpack(ret, 4) } -- 4 is again, dependant on identifiers declared
    end

-- ^ this works, but isn't nearly as pretty
-- it produces:
-- 1	1	nil
-- 2	2	nil
-- 4	4	nil
-- 5	5	nil

-- Considerations:

-- 1) you should be able to modify whatever stateful values the iterator passes to the body of the loop
-- 2) it should be noted that in order to interchangeably use generators, they must return at least 2 values on each successful iteration, some_table[k] = v is convention: for k, v pairs/ipairs({ 1, 2, 3 }) do some_table[k] = v end
-- 3) this way of writing the functional for loop should allow for iterators returning varargs

RecentChanges · preferences
edit · history
Last edited May 26, 2012 6:44 pm GMT (diff)