I have been playing with Ruby this week because Mike Stok has written a nice "Ruby for Perl Programmers" article for The Perl Review. I started to port Business::ISBN to Ruby, and the code is turning out to be a lot smaller and easier to read than the original Perl.
Some of this might be the Second Version Effect, but I do not think so. Ruby hides some of the things that I have to do explicitly in Perl. I am learning a lot about what I should have done with Business::ISBN, but I think Ruby has some definite advantages over Perl for this task. I am starting to think of it as all the good parts of Perl and Python without the bad parts of Perl or Python.
In the Perl Business::ISBN fix_checksum method, although I do not have to store the object in $self, it is better than working with $_[0]. In Ruby, the variable self is automatically the object which received the message. In Perl, I have to call methods with an explicit object, so even if I did not use $self, I would still have to use $_[0]->_checksum. In Ruby, the method is automatically called on the object, so a lot of typing disappears.
Another great thing about Ruby is that everything is truly an object, so strings have methods. Not only that, strings are sequences which I can treat sort of like Perl arrays---no need for substr()!
Since Ruby eliminates a lot of the typing, I also feel comfortable removing a lot of blank lines. Ruby does away with the curly braces and the semicolons, sort of like Python, without the fascist indention scheme. Ruby encourages lots of small, tight code mostly because its statements are terser.
The Perl fix_checksum
sub fix_checksum { my $self = shift;
my $last_char = substr($self->{'isbn'}, 9, 1);
substr($self->{'isbn'}, 9, 1) = $self->_checksum;
return $last_char eq $checksum; }
def fix_checksum before = @isbn[9..9] @isbn[9..9] = _checksum return before != @isbn[9..9] end
sub _checksum { my $self = shift;
my $sum = 0;
foreach( reverse 2..10 ) { my $index = 10 - $_; $sum += $_ * substr( $self->{isbn}, $index, 1 ); }
my $checksum = ( 11 - ( $sum % 11 ) ) % 11;
$checksum = 'X' if $checksum == 10;
return $checksum; }
def _checksum sum = 0 10.step( 2, -1 ) { |n| m = 10 - n sum += n * @isbn[m..m].to_i } checksum = ( 11 - ( sum % 11 ) ) % 11 checksum = 'X' if checksum == 10 return checksum.to_s end