-
-
Save headius/1315794 to your computer and use it in GitHub Desktop.
RESET_STACK = [] | |
SHIFT_MAP = {} | |
def reset(&block) | |
RESET_STACK.push block | |
block.call() | |
ensure | |
RESET_STACK.pop | |
end | |
def shift(&block) | |
if SHIFT_MAP.key? block.source_location | |
ary = SHIFT_MAP[block.source_location] | |
value = ary.pop | |
SHIFT_MAP.delete(block.source_location) if ary.empty? | |
value | |
else | |
shift_proc = proc do |arg| | |
SHIFT_MAP[block.source_location] ||= [] | |
SHIFT_MAP[block.source_location].push arg | |
RESET_STACK[-1].call | |
end | |
block.call(shift_proc) | |
end | |
end | |
p reset { 2 * 2 } # 4 | |
p reset { 2 * shift {|k| 2 } } # 4 | |
p reset { 2 * shift {|k| k[2] } } # 8 | |
p reset { 2 * shift {|k| k[k[2]] } } # 16 | |
p reset { 2 * shift {|k| k[shift {|j| j[3]}] } } # 24 | |
p reset { 2 * shift {|k| k[3] + k[4]} } # 28 |
@stephenb: Duh, thanks for the correction.
Yeah, of course this impl only works downstream from reset; you can't actually save off the continuation and call it from outside reset. That would only be possible with a library or JVM support for real continuations/coroutines.
My version obviously also deepens the stack for the nested continuation invokes, where a Lisp or Scheme VM would probably TCO some of those away.
I'm interested in understanding more about shift-reset and was looking for more interesting examples where it would be useful and came across Christian's article.
Then I was trying to work out how the two implementations differed and why his example didn't work with your implementation. Stepping through the code in the different implementations was a bit of a mind-bender. Haven't used continuations in Ruby before.
Does the way you've implemented shift-reset prevent creating some kind of lazy forkable iterator similar to what Christian made with each_to_stream(collection)
?
correction:
interesting reference: http://chneukirchen.org/blog/archive/2005/04/shift-reset-and-streams.html