Monday, August 16, 2010

Learning Ruby: Vampire Numbers

Instead of the traditional book group at work I'm in a group where each person chooses to learn a new language.  For me to make it feel worth while it either needs easy C bindings, existing bindings, or be used for scripting at work.  That left me with Go, Lua, Io, and Ruby.  Go was out due to error handling and generics.  I decided to stick with Ruby so it would be work practical (Python is heavily used by the company-wide build tools but my group has a lot of Perl and Ruby scripts).

Our first assignment was simple, identify vampire numbers.  They seem arbitrary and useless but oh well.

One of the first things I did was "gem install rubydoctest".  I like doctests from Python for their providing examples for the code and that they provide very low friction low coverage tests.

My work computer is sadly running Windows.  I did not have tool chains setup to install ruby-prof from source and I couldn't figure out how to install the pre-built version referenced from their site.

A quick summary of my initial thoughts
  • rubydoctest has poor error output but I love that "!!!" drops you into irb (though I didn't find out about it until I was done).
  • I was kind of surprised Ruby doesn't have a factorial function.  I'm not as surprised about a lack of a permutation function, Python's just special like that.
  • Very few examples show how to separate module logic from executable logic (Python's "if __name__ == "__main__" idiom)
  • Optional return's are ugly for most code.  The one place they work well is used inside of a block as a predicate for a function (like the comparator for sorting).
  • Optional parenthesis make it confusing what is a property or method.  Not knowing what is going on feels sloppy to me
  • I got thrown off by changing fairly standard names (exception handling, next/continue) and at first didn't think "next" existed.
  • Find it a strange concept to use next, break, retry, redo inside of blocks.  I'd be curious what the underlying principles are for how the block communicates with its caller to execute those.
  • Retry and redo seem cool though I am unsure how often I would ever use them
  • I like Python's separation of repr from str which makes debug output easy.  I was wanting to figure out what some lists I was using were doing but each element was printed on a separate line, making it take up a lot of space and making it hard to distinguish one array from another.  I didn't notice some of the pretty print functions till later and haven't had a chance to try them yet.
  • My program was slow and according to profiling most of it was in iterating.  (0...5).each was the fastest, next was 0.upto(5), and then last was doing the looping raw (I think).  All of that kind of surprised me, most especially how slow looping was in Ruby.  I was impressed the closures for blocks didn't add much overhead (or at least it isn't noticeable compared to how slow ruby is in general).
  • I kept mixing up whether I was working with an actual iterator and an Enumerable (and it took me a while to notice the difference)
  • I really miss the composable iterators of Python (enumerate plus everything in itertools)
  • Remembering  ".." vs "..." is annoying and I end up having to look it up every time (which is very bad for code readability).
  • A problem that drove me nuts for a while is "x = false or true".  The "=" has higher precedence than "or".  What I needed is "x = false || true".  A blogpost I later came across tries to frame "or" and "and" as flow control operators rather than logical operations. I think there are way too many operators.  Can you name the difference between ".."/"...", "=="/"==="/"eq?"/"equal?", etc?