A comprehension generator? |
That's right, I'm going to talk about one more upcoming language feature likely to be part of the ECMAScript 7 specification. If you like generator functions and you like array comprehensions, you're going to love Generator Comprehensions!
Maybe.
Not just another weird mashup, Generator Comprehensions have a syntax almost identical to Array Comprehensions, but create simple generator functions based on iterable objects instead of new arrays. Generator functions, if you recall, are a new type of JavaScript function which exit and re-enter while maintaining their state. If your generator function needs are very simple, creating one with a comprehension may be desirable.
Take a look at the simplest of examples to understand how they work. Important note! Just like with Monday's Array Comprehension examples, these only work on Firefox 30+. Sorry Chrome/Node fans.
var data = [ 1, 2, 3, 4, 5 ]; var gen = ( for (n of data) n * 2 ); console.log(gen.next().value); // prints 2 console.log(gen.next().value); // prints 4 console.log(gen.next().value); // prints 6
The Generator Comprehension appears on line 3. It looks almost exactly like an array comprehension, but uses parentheses ( ) instead of square braces [ ] to delineate it. Inside the parentheses, it takes the form for (n of iterable) statement, where the iterable in this case is an array and the statement is multiplying n by 2. The generator created will yield the result of the statement with the next value of n each time it is invoked. It is equivalent to this code:
var data = [ 1, 2, 3, 4, 5 ]; function* doubler(a) { for (var i = 0; i < a.length; i++) { yield a[i] * 2; } } var gen = doubler(data); console.log(gen.next().value); // prints 2 console.log(gen.next().value); // prints 4 console.log(gen.next().value); // prints 6
I suppose you'd have to agree that the comprehension version saves a few lines of code. And fewer lines means fewer chances to make a mistake, right? Maybe. But remember, if you overuse all this slick, super-short syntax, your code becomes less readable and therefore less maintainable. Trade-offs.
I should also mention that just like Array Comprehensions, the statement part of the expression can include an if. So if you wanted a generator that produced just the leap years from a given array of years, it might look like this:
var years = [ 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005 ]; var leapYears = ( for (y of years) if ( y % 4 == 0 && (y % 100 != 0 || (y % 100 == 0 && y % 400 == 0)) ) y ); console.log(leapYears.next().value); // prints 1996 console.log(leapYears.next().value); // prints 2000 console.log(leapYears.next().value); // prints 2004
Of course, cramming the leap year logic (divisible by 4 but not divisible by 100 unless also divisible by 400) into a single if statement just trades vertical code for horizontal code - and horizontal code really bothers me. Call me old-fashioned, but I like it when the lines are no more than 80 characters wide. But everyone is entitled to their own code style - unless you have to work on a team! Then you better do what's agreed upon by the group. Just sayin'.
Now for some commentary. While Generator Comprehensions are a fine way to create generators in a single line of code, I feel like the real power of generator functions is in having them encapsulate logic that can not (or should not) be expressed as a single line. If I just want to iterate over an array and even perform some basic translation of its values, I'm not sure that a Generator Comprehension would be my first choice. At the risk of sounding like a software dinosaur, there's nothing wrong with for loops when done correctly. Their intent is more clear, which makes maintenance less costly. I worry about those kinds of things because I'm a software engineer, not a hacker.
I'm also an author of frog comics, which means I worry about you clicking on the link below.
Amphibian.com comic for 7 October 2015 |
No comments:
Post a Comment