4
\$\begingroup\$

I have to sort some numbers with Perl. But some of the numbers are undefined. Perl's compare operator <=> treats undef as -inf 0. The result is, that the undefined numbers are at the beginning:

perl -MData::Dumper -e 'print Dumper(sort {$a <=> $b} 3, undef, 2, 1);'
$VAR1 = undef;
$VAR2 = 1;
$VAR3 = 2;
$VAR4 = 3;

I want them to occur at the end, because they represent new items, which should be added at the end. This means I am looking for a way to change the way Perl treats undef. In my case undef should be treated as +inf instead of -inf 0. I came up with the following expression.

($a <=> $b) * ((defined $a)*2-1) * ((defined $b)*2-1)

Is it always correct or did I miss anything?

And is there an easier way to achieve the same?

\$\endgroup\$
5
\$\begingroup\$

In fact, Perl treats undef as 0 in numeric contexts, and warns about that (why there's no -w in your one-liner?). You can use the defined-or operator to handle it:

sort { ($a // 'Inf') <=> ($b // 'Inf') } 3, undef, 2, 1

You need Perl v5.10+ for it to work, in older versions, you have to be more verbose:

(defined $a ? $a : 'Inf')
\$\endgroup\$
  • 2
    \$\begingroup\$ I have Perl 5.8.4 here and using defined in the conditional operator has no effect on the sort order. Perl seems to understand 'Inf' but treats it as zero: pastebin.com/VAAP4dMw \$\endgroup\$ – ceving Oct 7 '15 at 15:16
  • \$\begingroup\$ @ceving: You must have mis-coded it. It should look like sort { ( defined $a ? $a : 'Inf' ) <=> ( defined $b ? $b : 'Inf' ) } 3, undef, 2, 1 \$\endgroup\$ – Borodin Nov 29 '15 at 22:17
2
\$\begingroup\$

$a <=> $b will warn under warnings when one or both of variables is undef.

Thus, you can use short circuiting with || (high precedence OR operator), which will compare them only when both variables are defined (first two expressions evaluate to false),

sort { !defined($a)*1 || !defined($b)*-1 || $a <=> $b }

output

$VAR1 = 1;
$VAR2 = 2;
$VAR3 = 3;
$VAR4 = undef;
\$\endgroup\$
  • \$\begingroup\$ I think the result must be 0, if both are undefined. \$\endgroup\$ – ceving Oct 8 '15 at 7:30
  • \$\begingroup\$ @ceving you can my ($da,$db) = map {defined} $a,$b; ($da || $db)*1 && (!$da*1 || !$db)*-1 || $a <=> $b) for correctness, but sort result will not change. \$\endgroup\$ – mpapec Oct 8 '15 at 7:59

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.