Yakov Dan
Yakov Dan

Reputation: 3372

issue when sorting a list in perl

I have a list of strings which denote time values, i.e. 1us, 100ps, 10s, 7fs (microseconds, picoseconds, seconds, femtoseconds, etc). I need to sort the list in ascending order. To do that, I wrote a comparison routine which when given two strings, translates each string into the corresponding numerical value and compares them. Still, the list is not sorted correctly. After much debugging, I'm at a loss. Here's my code:

#!/usr/bin/perl
use warnings;
use strict;
   my %scales = ('fs'=>1e-15,'ps'=>1e-12,'ns'=>1e-9,'us'=>1e-6,'ms'=>1e-3,'s'=>1);
   my @times = ('1s','7ns','100ps','500ms','9us');
   my @stimes = sort bytime @times;
   sub bytime {

      my $op1 = $a;
      my $op2 = $b;
      $op1 =~ /\b(\d+)([munpf]*s)\b/;
      my $v1 = $1 * $scales{$2};
      $op2 =~ /\b(\d+)([munpf]*s)\b/;
      my $v2 = $1 * $scales{$2};
      return $v1 > $v2;
}

print "@times"."\n"."@stimes"."\n";

The result I get is:

1s 7ns 100ps 500ms 9us

100ps 7ns 500ms 1s 9us

which is clearly wrong, even though some sorting takes place. What's going on?

Upvotes: 1

Views: 71

Answers (1)

Silvar
Silvar

Reputation: 705

Your error is on the line

return $v1 > $v2;

It will return 1 (true) if $v1 is greater than $v2 and 0 (false) in all other cases.

You want to instead use the numerical comparison operator <=>:

return $v1 <=> $v2;

which will return 1, 0 or -1 depending on the comparison result and will allow the sort to do its work correctly. (If you were dealing with strings instead of numbers, the operator would be cmp.)

Complete working code:

#!/usr/bin/perl
use warnings;
use strict;

my %scales = ('fs'=>1e-15,'ps'=>1e-12,'ns'=>1e-9,'us'=>1e-6,'ms'=>1e-3,'s'=>1);
my @times = ('1s','7ns','100ps','500ms','9us');
my @stimes = sort bytime @times;

sub bytime {
    my $op1 = $a;
    my $op2 = $b;
    $op1 =~ /\b(\d+)([munpf]*s)\b/;
    my $v1 = $1 * $scales{$2};
    $op2 =~ /\b(\d+)([munpf]*s)\b/;
    my $v2 = $1 * $scales{$2};
    return $v1 <=> $v2;
}

print "@times"."\n"."@stimes"."\n";

Output:

1s 7ns 100ps 500ms 9us
100ps 7ns 9us 500ms 1s

Upvotes: 7

Related Questions