Skip to content

Instantly share code, notes, and snippets.

@aaronj1335
Last active December 26, 2015 15:09
Show Gist options
  • Save aaronj1335/7170272 to your computer and use it in GitHub Desktop.
Save aaronj1335/7170272 to your computer and use it in GitHub Desktop.
how generators work w/ iterating over collections.

generators

2 (most?) common uses of generators:

  • async control flow
    • this is what the js community has mostly been talking about
    • that's a huge topic, and it's better covered elsewhere, so i'm not going to go over that stuff here
  • iteration - this is probably the most common use case for python

iteration

i've got a comparison of iteration techniques between generators (how python does it), and callbacks (currently the preferred technique in js). they look kinda similar. take a minute to study both.

all done? ok here's the similarities and differences i see:

stuff that's good about both of them

  • the internal state of iteration is abstracted

    • i.e. the "our code" sections don't have to understand how odd numbers work
    • so both are an improvement over the C-style for loops
  • they both enable efficiently iterating over huge collections

    • odd numbers are kind of trivial, but you can see how these could be used for incrementally processing a big file or something

reasons callbacks are good

  • it's just a function

    • everybody knows how they work
    • they make great building blocks for libs like underscore
  • provides a new scope so you can have variables local to the inside of the loop

    • you avoid problems like this
    • es6 let statements accomplish this, so this won't be as important in the future

reasons generators are better for this

  • this changes, so instance methods like the one below require extra boilerplate (contrived example):

     Model.prototype.values = function() {
       var values = [];
    
       this._properties.forEach(function(prop) {
         // DOH! "this" is no longer our Model instance
         values.push(this.get(prop));
       });
    
       return values;
     };
    
  • you can't break out of the loop early

  • stepping through it in a debugger isn't as easy

    • you have to either step into the forEach method (not even possible if it's natively supported)
    • or set a breakpoint in the callback
  • callbacks don't compose

    • with generators, you can consume the output of one in another

           def factors_of_three(gen):
               for x in gen:
                   yield x % 3 == 0
             
           # odd factors of three
           for x in factors_of_three(odd_numbers)):
               print x
      
    • also consider filtering a huge array, then mapping the values:

      • using underscore, _.map(_.filter(arr, myCondition), myTransform), you create two copies of the array (albeit they're smaller since they're filtered)
      • using python generators (my_transform(x) for x in arr if my_condition(x)) you haven't created any copies

language support

part of the reason generators are so handy in python is that it has great language support. python's for...in and list comprehensions understand how to consume generators, where as for...in in javascript does not. luckily the es6 standard that defines generators also has for...of and array comprehensions that work with generators, but the tricky thing is we don't know when browsers will roll out support for them.

// "library" code
function oddNumbers(cb) {
var i = 1;
while (true) {
cb(i);
i++
}
}
// "our" code
oddNumbers(function(n) {
console.log(n);
})
# "library" code
def odd_numbers():
i = 1
while True:
yield i
i += 2
# "our" code
for n in odd_numbers():
print n
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment