Function.Iterator.js - Copyright (c) 2010 Olmo Maldonado
I love generators, or at least I think I do. I first learned about generators from Python's PEP 255.
In my own words, generators constructs iterators. Python and Mozilla's JavaScript 1.7 have similar syntax for generators. Unless you're using Rhino, however, you're not going to see that sugar any time soon in a portable environment.
I've been trying different variations of a good (generalized) solution that fills this portability gap, because I want to use generators -- or rather iterators today.
Keep in mind this is yet one variation, and I'd love for someone to come up with an awesome_er_ solution.
Check out the Specs for usage and also follow the How to Use section in this file.
Build via Packager, requires MooTools Core to be registered to Packager already (ty cpojer)
./packager register /path/to/Function.Iterator.js
./packager build Funciton.Iterator.js/* > Function.Iterator.js
The premise is simple. You either add all the functions to the arguments of the constructor (Function.Iterator
) or you dynamically add more functions to the iterator.
The first case is pretty basic, and not too exciting. Here's an example:
var iter = new Function.Iterator(function(){
return 'a';
}, function(){
return 'b';
}, function(){
return 'c';
});
iter.each(function(character){
console.log(character);
});
Not exciting, like I said. Things get fun, though, when you push more functions.
iter.push(function letterD(){
return 'd';
});
// since we called each method, we're at the end of our list of functions
// and iter.next() is the letterD function
console.log(iter.next()); // 'd'
// every call to each method resets back to the beginning
iter.each(function(character){
console.log(character);
});
Here's the fun part. What do you think would happen if I did the following?
var counter = 0;
var iter = new Function.Iterator(function next(){ // name your method instead of using argument.callee (deprecated in ES5)
iter.push(next);
counter++;
});
iter.next(); // counter = 1
iter.next(); // counter = 2
iter.next(); // counter = 3
// ...
// ...
iter.next(); // counter = n
If you've answered it's an Endless, or non-stopping, or w00t generator than you'd be right.
CAREFUL If you're not careful, you will have an endless -- and non responsive situation.
var iter = new Function.Iterator(function next(){
iter.push(next);
});
var result;
while (result = iter.next()) console.log(result); // you'll never stop!
As always the onus falls on the developer to watch out for these cases. You are given power, thus it is your responsiblity.
Normal Array.forEach
and even Object::forEach
in MooTools have no way of stopping your loops until they're finished.
Function.Iterator
, however, comes with a nice Function.Iterator.stop
function. This function will throw an Exception which the each
method automatically catches to stop the looping.
To be clear: if you're not using each
and you call too many next
your script will throw an exception that needs to be handled.
var iter = new Function.Iterator(function(){
return 'first';
});
iter.next(); // 'first'
iter.next(); // StopIteration Error!
var iter = new Function.Iterator(function(){
return 'first';
});
try {
while (true) iter.next();
} catch (StopIteration){
}
// script continues without disrupting the control flow
This gives stop
its power. Use wisely:
var counter = 0;
var iter = new Function.Iterator(function next(){
iter.push(next);
counter += 5;
});
iter.each(function(){ // yes you can still use each
if (counter > 20) iter.stop();
});
// or old school
// option A
counter = 0;
try {
while (counter < 20) iter.next();
} catch (StopIteration){
}
//option B
counter =- 0;
try {
while (true){
iter.next();
if (counter > 20) iter.stop(); // or break;
}
} catch (StopIteration){
}
- More meaningful examples: fib, co-routines
- More helpful sugars:
skip
,continue