lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


On Sat, Jun 16, 2012 at 5:58 AM, jseb <gmane2010@finiderire.com> wrote:
> Hello,
>
> I try to print elements of this table:
>
> local big =
>  { foo = { "sub1_foo","sub2_foo"  },
>    bar = { "sub1_bar", "sub2_bar" }
>  }
>
> I'd like to get elements in this format:
> "foo","sub1_foo"
> "foo","sub2_foo"
> "bar","sub1_bar"
> "bar","sub2_bar"
>
> So, i've thought it was a nice day to write a closure.
>
> But the closure always returns the same thing ("bar" with it first element).
>
> I know i have the same problem with tables without embedded tables, but on
> those tables, you probably told me to use «for i,v in pairs(t)…». That's why
> i've made made it more difficult.
> And i like to solve it with a closure.
>
>
> Please, have a look:
>
>
> #!/usr/bin/env lua
>
> Thank you.
>
>

Hey jseb,

Coroutines are a fun way to do nested iterators, but my flight is
delayed and I'm still smarting from my recent dumb question to this
list. So, I took a crack at the closures version...

--BEGIN CODE

function iter (t)
	local k, v,sk, sv --= next(v)
    local function iter_func() -- can't use syntax of 'iter_func =
function() unless you declare 'local iter_func()' prior
		if sv == "Pigs fly." then
			error("The world has ended.")
		end
		if type(sk) == "nil" then
			k, v = next(t, k)
			if k then
				sk, sv = next(v, sk)
				return iter_func() --< Why I didn't do an anonymous funciton
			end
		else
			print (k .. " -> " .. sv)
			sk, sv = next(v, sk)
		end
	end
	
	local function make_arrays_start_with_zero()
		sv = "Pigs fly." -- Illustrating that non-locals change, even after
closure is created.
		return nil
	end
	return iter_func, make_arrays_start_with_zero
end

fx, array_zero =iter(big)
fx() -- «bar -> sub1_bar»
fx() -- «bar -> sub1_bar» (again)
fx() -- «bar -> sub1_bar» (again)
fx() -- «bar -> sub1_bar» (again)
fx() -- should be nothing...

array_zero()
fx()
--should error
--END CODE

The trouble with your crack at it is that pairs is initialized with
each call and won't keep any state between calls, as there is no
iterator value being closed over for it to hang on to (nor is there a
way for you to give pairs one, as it is the job of the iterator to
deal with the table progression).

Remember that a closure a function, plus anything else that it needs
from the calling environment, such as its locals, called "non-locals"
in your closure, or that environment's non-locals, etc.

So, the critical extra sauce from my run at it was "local k, v,sk, sv"
and then NOT re-declaring them as local within the closure. Had I done
so, they would have lost their persistant state.

I'm trying to be as helpful as I can, so forgive my restating things,
but I'll give one more go at it. In my example, when I called iter():

1: it makes new locals called "k,v,sk,sv" and also a new local
function called "iter_func()."
(note: I don't define it as an anonymous function because I have a bit
of recursion going on and so I need the function to exist with a name
in order to re-call it)
2: The new closure is returned, which is the function "iter_func",
plus its non-local variables "k, v, sk, sv", which upon creation are
all nil, and also "iter_func", which is there for my evil
loop-avoiding-recursive madness.
3: When I called the closure, it ends up setting k,v,sk and sv.
Because they are not re-declared as local variables, they remain
non-local during and after the call. Therefore, they stick around as
long as there are any closures still referencing them, although you
can't access them from outside of the closure.
4: Every time I call the new closure, it re-creates/resets everything
local to the closure, but again, the non-locals are what they were the
last time it was called.

Another note about closures: they're not just for iterators. As you
can see from my pithy example, I change one of the non-locals that are
enclosed within the generator. Since you can have non-locals that are
shared between multiple closures, you can't know what the values of
non-locals are by simply looking at the logic of only one closure. In
this way, closures  are often used to create object-oriented programs.



Best Regards,

Andrew Starks
Tightrope Media Systems