Thursday, March 12, 2009

Speaking of Perl, Ruby and sorting, sort in both Perl and Ruby allow you to specify a code block to implement your own arbitrary sorting routine. Perl's documentation shows you how you can define this as a subroutine and just reference the subroutine when you call sort. This is handy both for re-use and when your sort routine is complex and doesn't easily fit on one line. Ruby's sort documentation? Eh, no mention of such a thing.

In fact it is possible, but delves into a whole area of Ruby that is somewhat under-documented. The trick is that sort wants a code block, but code blocks are not first class entities. You can't name and reference a code block. However, Ruby has a couple of similar entities, Proc and lambda, that can be converted to/from code blocks and can be named and referenced. The unary ampersand operator will convert a Proc or lambda to a code block. The syntax works like this:

names = [
  {:fname => 'John', :lname => 'Smith'},
  {:fname => 'Jane', :lname => 'Smith'} ]


sorter = lambda do |a,b|
  [a[:lname], a[:fname]] <=> [b[:lname], b[:fname]]
end

names.sort(&sorter)

Wednesday, March 11, 2009

When sorting complex structures in Perl is is common to use syntax like:

sort { $lname{$a} <=> $lname{$b} || $fname{$a} <=> $fname{$b} } @names

The Perl sort documentation lists examples with this syntax. True to form the Ruby sort documentation doesn't show examples of anything that complex. Best I can tell anything like this doesn't work in Ruby:

a <=> b || c <=> d

Any way I write that I get back the result of the first comparison, even if the result is zero (which should trigger a fall-through to the second comparison).

Since Ruby's Array implements <=> you can achieve the desired result with:

[a, c] <=> [b, d]

Wednesday, March 04, 2009

A couple of MacPorts errors I just encountered:

Error: Unable to execute port: can't read "frameworks_dir": no such variable

I had version 1.6 and it seems the recommendation to fix this is to update to 1.7 (via port selfupdate). I started port in interactive mode, ran selfupdate, then tried my install again and got:

Error: Unable to open port: can't read "porturl": no such variable

I wasn't sure if it was necessary, so I ran 'sync' at this point. The install then failed with:

Error: Unable to execute port: can't read "PortInfo(name)": no such element in array

Couldn't find any info about that online, so on a lark I exited port and restarted it, after which the install proceeded.