C#
More C#, Ruby, and Python Iterators, and JavaScript too
Making a valiant attempt to post code with my comment system (Sorry, Mike! :-(), Mike Roome points out:
The ruby example isn’t exactly equivalent, from what i understand of ruby – a block defines an anonymous function, which is passed into the method being called, and can be called within the function with ‘yield’. The end result is more or less the same, but the method by which the result is achieved is completely different.
That’s a good point. Ruby and Python’s iterators look so much alike when you use them that I completely forgot that they are quite different behind the scenes.
They certainly don’t look all that different if you compare these simple examples in Python:
and Ruby:
The loop that uses MyIterator is coded differently, but the MyIterator function itself is identical in both languages (ignoring the trivial lexical differences).
Let’s look at the bytecode generated for the Python version, using Python’s dis disassembler:
The MyIterator function is simplicity itself, a direct transliteration of the source code:
3 YIELD_VALUE
3 4 LOAD_CONST 2 ('Second')
7 YIELD_VALUE
4 8 LOAD_CONST 3 ('Third')
11 YIELD_VALUE
12 LOAD_CONST 0 (None)
15 RETURN_VALUE
The code for the for s in MyIterator(): print s loop isn’t much more complex:
3 LOAD_GLOBAL 0 (MyIterator)
6 CALL_FUNCTION 0
9 GET_ITER
>> 10 FOR_ITER 11 (to 24)
13 STORE_FAST 0 (s)
3 16 LOAD_FAST 0 (s)
19 PRINT_ITEM
20 PRINT_NEWLINE
21 JUMP_ABSOLUTE 10
>> 24 POP_BLOCK
Of course, this doesn’t tell us anything about how much work the Python interpreter has to do to implement YIELD_VALUE, FOR_ITER, and the like, but it does show that they’re built in at the bytecode level.
It would be interesting to see Ruby’s implementation, but Ruby uses an Abstract Syntax Tree instead of bytecode. Does anyone know how to inspect Ruby’s AST?
Mike continues:
For what it’s worth, you could semantically equivalent to the ruby example in C# as well, using anonymous delegates.
delegate void Action(T obj); is a standard delegate provided by the 2.0 class library. Others include: * bool Predicate(T obj); * U Converter(T from); * int Comparison(T x, T y);
see http://blogs.msdn.com/kcwalina/archive/2004/06/22/162533.aspx for more details
also, since ‘yield’ isn’t a keyword on its own (it’s only special in a ‘yield return’ or ‘yield break’ statement), i decided to call the delegate ‘yield’, to make the correspondence to the ruby clearer:
That looks just like some of my JavaScript code! Here’s how I’d write it in JavaScript. (The console.println() calls are for Adobe Acrobat, but the rest of the code is generic JavaScript.)
We use a lot of code like this in Acrobat JavaScript, for example this monitor filter function from media.js, the multimedia JavaScript extensions in Acrobat 6:
(This is a somewhat cleaned-up version of the code from media.js, the way I wish I’d coded it. :-))
In Ruby, this would be:
That’s sweet and simple! The beauty of Ruby’s code blocks is that they make it so easy to use inline anonymous functions with a minimum of cruft.
Iterators in C#, Python, and Ruby
Matt Pietrek marvels at C# 2.0 iterators and dissects them right down to the CLR bytecode. I always learn something from Matt, and this whirlwind tour is no exception.
Matt says, “This was the beginning of my descent into the loopy world of C# 2.0 iterators. It took me awhile to wrap my head around them, and when I tried to explain them to other team members I got looks of total confusion.” I wonder if it would have been less confusing if Matt’s team had first been exposed to yield iterators in a language that makes them easier to use.
After using Python and Ruby, the iterators in C# feel right at home to me. They work the same in all three languages, but in Ruby and Python there’s not as much other code to get in the way of understanding them.
Let’s combine all of Matt’s examples into one, and compare the code in each language. First, in C#:
When you run that, it should print:
First
After First
0
1
2
Before Last
Last
After Last
Here’s how you would write the same code in Python:
And in Ruby, the code looks like this:
The one unfamiliar thing here may be the |name| notation, which is how a code block such as the body of a loop receives its argument. And the p statements are a kind of print statement.
This Ruby version is even more concise and equally readable once you’re comfortable with the |name| notation:
Either way, the Python and Ruby versions make it easier to see what the iterator function does and how yield interacts with the rest of the code.
You may note that the Python and Ruby versions don’t create and instantiate a SomeContainer class as the C# version does. That’s true, and it would make the code in those languages a bit longer (but still simpler than the C# code). But, if you don’t need to—and you especially don’t need to when you’re experimenting and trying to understand a radical new technique like yield iterators—why bother?
RSS 2.0