For a couple years now, I’ve been a big Python fan. I’ve been using Ruby at work for the past month or so, and it’s beginning to grow on me. At first, I didn’t like that there were multiple ways to do one thing, often with only slightly nuanced differences. For instance, the
.count methods on enumerables perform essentially the same function, but apply in different ways. Similarly,
ARGF all refer to standard error. I also thought that optional parentheses around function arguments and question marks and exclamation points in method names were strange.
But now that I’ve used Ruby a little more, I’m beginning to like certain constructs and missing them when I write Python. The following idiom (rather reminiscent of Perl), which prints lines from
stdin is kind of fun, even though it’s a little ugly:
while gets puts $_.strip end
What I really like about Ruby, though, is the extra support it has for functional programming. Python has filter, map, reduce and nice list comprehensions, but certain Ruby statements return values when Python ones don’t, making functional things more difficult to write. For example, iterating over a list of tokens and producing a hash of the count of the occurrence of each token can be accomplished with the following:
foo_list.inject(Hash.new(0)) do |hash, item| hash[item] += 1 hash end
Hash.new(0) creates a hash that returns 0 for keys that don’t already exist in the hash, making this an ideal data structure for counting frequencies of objects.
inject is Ruby’s reduce. Ruby allows multiline “code blocks”, which are anonymous functions. The line “hash” returns the updated hash after each iteration of “inject”. (Ruby functions and code blocks treat the last statement as a return without needing an explicit return keyword.)
In contrast, Python’s
dictionary[item] += 1 does not return anything, so to implement something similar, we have to either do it with an explicit loop, or write a class whose
increment method returns itself:
from collections import defaultdict class FooDict: def __init__(self, init): self.hash = defaultdict(lambda: init) def increment(self, key, incr): self.hash[key] += incr return self reduce(lambda hash, item: hash.increment(item, 1), foo_list, FooDict())
which is considerably less elegant.
If you know of a better way to do this, please let me know!