Created
March 5, 2012 08:29
-
-
Save shesek/1977471 to your computer and use it in GitHub Desktop.
CoffeeScript generator function decorator
This file contains hidden or 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
# Very simple implementation for generators in CoffeeScript. Obviously not as powerful as real generators, | |
# and only useful for iterating over values (pulling values via `.next()` is quite impossible to implement | |
# with a natural `yield` syntax in the generator), but still quite useful and provides an elegant syntax. | |
# The `generator` function is used as a decorator for functions. The function passed to `generator` gets | |
# called with the `this` context having a `yield` property that should be used to yield values to consumers. | |
# The function returned by calling `generator` should than be called with a function to consume the values. | |
generator = do -> | |
StopIteration = {} | |
stopCtx = stop: -> throw StopIteration | |
(gen) -> (fn) -> | |
try gen.call yield: (args...) -> fn.apply stopCtx, args | |
catch e then throw e unless e is StopIteration | |
# Usage: | |
range = (min, max) -> generator -> @yield i for i in [min..max]; return | |
(range 5, 15) (num) -> console.log num | |
# Infinite generators can be stopped by calling `@stop` in the function consuming the values: | |
fib = generator -> | |
[prev, curr] = [0, 1] | |
loop | |
[prev, curr] = [curr, prev+curr] | |
@yield curr | |
return | |
fib (n) -> | |
console.log n | |
do @stop if n > 50 | |
## Or, if `this` needs to be preserved, `yield` and `stop` can be passed as arguments instead | |
# (`yield` is reserved in strict mode [and about to be used in harmony], so a different name should probably be used if its passed as an argument) | |
generator = do -> | |
StopIteration = {} | |
stop = -> throw StopIteration | |
(gen) -> (fn) -> | |
try gen.call this, (args...) -> fn.apply this, [stop, args...] | |
catch e then throw e unless e is StopIteration | |
range = (min, max) -> generator (yield) -> yield i for i in [min..max]; return | |
(range 5, 15) (stop, num) -> console.log num |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment