@yojimbo2000 - what you are calling a function baker is technically called a closure, and they are very useful, I agree. The Lua docs have some good examples.
Don’t you love it when you write some code, and it works, but for a completely different reason to why you thought it was working?
Oh yes, I’ve done that more than once
One of the things I’ve gleaned from reading about lua on the web is that coroutines and closures seem to be regarded as the real strengths of the language. Both can be thought of as functions-with-memory.
I use a closure in my utf8
library to iterate over characters. The point of using the closure is that the iterator can remember where the previous invocation was and therefore return the next character.
As mentioned above, I use coroutines in my Penrose Tile program because I have something that I want to do which takes a long time (compared to the ideal frame rate). So I want to split it up over frames, but I don’t want to have to set it up and tear it down each time. Using a coroutine allows me to pause the task, return control to the draw
function, and then resume it on the next cycle. I can then have several processes happening effectively simultaneously and each handles its own state so the central delegator doesn’t have to know the fiddly details of what each is doing. So coroutines are useful when you have things happening that aren’t necessarily in sync with the main draw cycle.
Coroutines are also very powerful when working with iterators. Last year, I was working on something that involved iterating over some stuff that wasn’t numerical. That is, there wasn’t an easy way to say what the kth term was in the list, but given the kth term it was easy to work out what the k+1th term should be. So again I wanted to remember the previous term, work out the next one, and then do something with that term. Once I’d done whatever I wanted to do, I’d start again. With a coroutine, this was so simple because I could “return” from the middle of a loop or a recursive function call no matter how deep it was.
Here’s a snippet of the code.
function deep_copy_into(t,tt)
tt = tt or {}
for k,v in ipairs(t) do
if type(v) == "table" then
table.insert(tt,deep_copy_into(v))
else
table.insert(tt,v)
end
end
return tt
end
function __subset(t,r,n)
if n == 0 then
coroutine.yield(deep_copy_into(r))
else
for j=1,2 do
table.insert(r[j],t[n])
__subset(t,r,n-1)
table.remove(r[j])
end
end
end
function subsets(t,n)
n = n or table.getn(t)
return coroutine.wrap(function() __subset(t,{{},{}},n) end)
end
The first function is a utility function, it copies one table into another, recursing into subtables (the assumption is that every table is an honest table – not a class – and every non-table is something like a string or number that can be copied with no side effects).
The second two functions are the key. The second sets up the coroutine, the first is the one that does the work. Note that what it does is either it yields the coroutine or it calls itself after modifying its arguments. So it keeps calling itself until it hits the condition for the coroutine yield, whereupon it returns control to the original calling function without needing to work back through all the iterated calls. Then when the calling function wants the next term, it resumes the coroutine which picks up where it left off.
The recursion combined with the need to remember where you are make this ideal for coroutines.
@LoopSpace - thanks for sharing that.
I have used coroutines to spread a long setup process over several frames, so the program doesn’t appear to freeze.
And since you can’t stop and start any process faster than once per frame (well, you can, but it’s unlikely you’d would), this seems to be the obvious use of coroutines - spreading a job over multiple frames.
I also have used coroutines for precisely that purpose, in my sorting algorithms demo:
https://gist.github.com/anonymous/2bada54b7a7fb11243d1
This is somewhat unrelated to Codea, but another area where coroutines are very useful - is server-side scripting. For example, Openresty server ( http://openresty.org ) uses coroutines to run several concurrent Lua scripts that service web requests, all inside a single-threaded nginx worker process.
As well as saying that a closure is a mini-class, looking at the Codea’s class code, it uses a closure to create each instance of a class (I think?)
-- expose a constructor which can be called by <classname>(<args>)
local mt = {}
mt.__call = function(class_tbl, ...)
local obj = {}
setmetatable(obj,c)
the __call
function is a closure, which remembers both the local obj
table, and the upvalue local c
metatable (or does setmetatable
create its own independent copy of c
?).
-- Class.lua
-- Compatible with Lua 5.1 (not 5.0).
function class(base)
local c = {} -- a new class instance
if type(base) == 'table' then
-- our new class is a shallow copy of the base class!
for i,v in pairs(base) do
c[i] = v
end
c._base = base
end
-- the class will be the metatable for all its objects,
-- and they will look up their methods in it.
c.__index = c
-- expose a constructor which can be called by <classname>(<args>)
local mt = {}
mt.__call = function(class_tbl, ...)
local obj = {}
setmetatable(obj,c)
if class_tbl.init then
class_tbl.init(obj,...)
else
-- make sure that any stuff from the base class is initialized!
if base and base.init then
base.init(obj, ...)
end
end
return obj
end
c.is_a = function(self, klass)
local m = getmetatable(self)
while m do
if m == klass then return true end
m = m._base
end
return false
end
setmetatable(c, mt)
return c
end