Reputation: 3372
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
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