Archive for February, 2010

Passing Perl regexes as arguments

I often find myself wanting to pass a regex as and argument to some function or another with the initial idea of passing the regex either as a string or a compiled regex (see the qr operator) and I invariably end up with problems. One example is for instance when I pass it as a string I mess up quoting ? / * . etc..

Another example of problems with this approach is the following, imagine that you have written a function to take a regex and you imagine searching all cells in an Excel sheet for cells matching this regex, as follows:

sub find_in_worksheet {
  my $regex = shift;
  my $workbook = shift;
  my $worksheet_name = shift;
  my $worksheet  = $workbook->worksheet($worksheet_name);

  my ( $row_min, $row_max ) = $worksheet->row_range();
  my ( $col_min, $col_max ) = $worksheet->col_range();

  for my $row ( $row_min .. $row_max ) {
    for my $col ( $col_min .. $col_max ) {

      my $cell = $worksheet->get_cell( $row, $col );
      next unless $cell;

      if( $cell->unformatted() =~ $regex ) {
	return ($row, $col);
      }
    }
  }
  return undef;
}

That works fine, until inevetably you want the cell contents NOT to match the regex… *sigh*, what now…?
Well it turns out that a much more fruitful (and a more general) approach is as follows:

sub find_in_worksheet {
  my $predicate = shift;
  my $workbook = shift;
  my $worksheet_name = shift;
  my $worksheet  = $workbook->worksheet($worksheet_name);

  my ( $row_min, $row_max ) = $worksheet->row_range();
  my ( $col_min, $col_max ) = $worksheet->col_range();

  for my $row ( $row_min .. $row_max ) {
    for my $col ( $col_min .. $col_max ) {

      my $cell = $worksheet->get_cell( $row, $col );
      next unless $cell;

      if( $predicate->($cell->unformatted()) ) {
	return ($row, $col);
      }
    }
  }
  return undef;
}

You would call this functions as follows:

my ($row, $col) = find_in_worksheet( sub { $_[0] =~ /regex to match cell contents/ } , $workbook, $worksheetname);

See the “sub { $_[0] =~ /regex to match cell contents/ }” part? There’s the magic. Now if you later on figure out that you want the cell contents NOT to match, you just call the method thusly:

my ($row, $col) = find_in_worksheet( sub { $_[0] !~ /regex to match cell contents/ } , $workbook, $worksheetname);

Volá!

Now you can even change the code for the predicate to something completely different, maybe not even involving regexes… for instance:

my ($row, $col) = find_in_worksheet( sub { $_[0] == 10 } , $workbook, $worksheetname);

This is just a small example of the power of using “sub’s” as arguments to functions and the very nice syntax in Perl for achieveing it. It also carries the benefit of giving you compile time checking of your regex! (Yes, Perl is a compiled language!)

Tags: , ,

Perl date difference

As a followup on date calculations, here’s small one to calculate the number of days between two dates, the same disclamer as for the previous post applies, so, without further ado, here goes:

# Given two dates, calculate the number of days between them
sub diff_dates {
  my ($sec1,$min1,$hour1,$mday1,$mon1,$year1) = @_[0..5];
  my ($sec2,$min2,$hour2,$mday2,$mon2,$year2) = @_[6..11];
  my $time1 = timegm($sec1,$min1,$hour1,$mday1,$mon1,$year1);
  my $time2 = timegm($sec2,$min2,$hour2,$mday2,$mon2,$year2);
  my $diff_seconds = $time1 - $time2;
  my $daydiff = $diff_seconds / 24 / 3600;

  return int( $daydiff );
}

Tags: , ,

Perl calculate X days forward or backwards

Ok, here’s a small snippet for calculating a new date given a date and the numer of days forward or backwards from that date without having to include Date::Time or some other big date handling lib which is not part of the standard perl distro. It wouldn’t surprise me if there are some subtile problems with this way of doing it, maybe leap years or something will mess it up, but I have found that it works fine for the most for me at least YMMV!

The format of the input is of course according to timegm.

# Given a date and the number of days forward or backwards (negative) calculate that date
sub calc_date {
  my ($sec,$min,$hour,$mday,$mon,$year) = @_[0..5];
  my $diff_days = $_[6];
  my $time = timegm($sec,$min,$hour,$mday,$mon,$year);
  my $diff_seconds = $diff_days * 24 * 3600;
  my $end_time = $time + $diff_seconds;

  return localtime($end_time);
}

Tags: , ,

Firefox download manager

Just discovered the other day that the Firefox “Downloads” (“Filhämtaren”) thingy is really a full fledged “Download Manager”, i.e you can drag-and-drop url’s on it and it will download them for you. Now, maybe it is only me in the whole world that didn’t know that, but from time to time I have been looking for a simple download manager to solve this particular problem for me, without realizing I had one under my nose the whole time. 🙂

Tags: , ,

Perl file slurping…

There are two basic alternatives for file slurping:

my @lines = <FH>;

Or…

{
    local( $/, *FH ) ;
    open( FH, $file ) or die "sudden flaming death\n";
    $text = <FH>;
}

A small comment on Perl 6, it has finally managed to get it right!

 my $content = slurp $filename;
# or
my @lines = slurp $filename;

Does it get easier than that? Shall we look at the Java code for the corresponding operation? No better not, the post will become so long that noone will have time to read it!

Tags: ,