http://www.perlmonks.org?node_id=1042707

Anonymous Monk has asked for the wisdom of the Perl Monks concerning the following question:

Hello Perl Monks, I am trying to understand the mechanics of string comparison in perl. The examples basically all show the same as this example I found online:

cmp Compare:
'a' cmp 'b' # -1
'b' cmp 'a' # 1
'a' cmp 'a' # 0

eq Equal to:
'a' eq 'b' # 0
'b' eq 'a' # 0
'a' eq 'a' # 1

ne Not-Equal to:

'a' ne 'b' # 1
'b' ne 'a' # 1
'a' ne 'a' # 0

lt Less than:
'a' lt 'b' # 1
'b' lt 'a' # 0
'a' lt 'a' # 0

le Less than or equal to:
'a' le 'b' # 1
'b' le 'a' # 0
'a' le 'a' # 1

gt Greater than:
'a' gt 'b' # 0
'b' gt 'a' # 1
'a' gt 'a' # 0

ge Greater than or equal to:
'a' ge 'b' # 0
'b' ge 'a' # 1
'a' ge 'a' # 1

So I took this thing and enclosed it in print "\n";. The code now looks like this:

sub main() { print "cmp Compare:\n"; print 'a' cmp 'b'."\n"; # -1 print 'b' cmp 'a'."\n"; # 1 print 'a' cmp 'a'."\n"; # 0 print "\n"; print "eq Equal to:\n"; print 'a' eq 'b'."\n"; # 0 print 'b' eq 'a'."\n"; # 0 print 'a' eq 'a'."\n"; # 1 print "\n"; print "ne Not-Equal to\n"; print 'a' ne 'b'."\n"; # 1 print 'b' ne 'a'."\n"; # 1 print 'a' ne 'a'."\n"; # 0 print "\n"; print "lt Less than\n"; print 'a' lt 'b'."\n"; # 1 print 'b' lt 'a'."\n"; # 0 print 'a' lt 'a'."\n"; # 0 print "\n"; print "le Less than or equal to\n"; print 'a' le 'b'."\n"; # 1 print 'b' le 'a'."\n"; # 0 print 'a' le 'a'."\n"; # 1 print "\n"; print "gt Greater than\n"; print 'a' gt 'b'."\n"; # 0 print 'b' gt 'a'."\n"; # 1 print 'a' gt 'a'."\n"; # 0 print "\n"; print "ge Greater than or equal to\n"; print 'a' ge 'b'."\n"; # 0 print 'b' ge 'a'."\n"; # 1 print 'a' ge 'a'."\n"; # 1 print "\n"; }
The desired results are already given in the example. In contrast, when I run the program, my shell tells me the following:
D:\perl>perl StringCompare.pl cmp Compare: -11-1 eq Equal to: ne Not-Equal to 111 lt Less than 11 le Less than or equal to 11 gt Greater than 1 ge Greater than or equal to 1
Now I'm all flabbergasted. What's going on there? As it doesn't print the '0's, it seems the only thing that gives the right number of '1's is "Less than or equal to". Everything else seems to do something unexpected. What did I do wrong here?

Replies are listed 'Best First'.
Re: Newbie flabbergasted by string compare results
by Athanasius (Archbishop) on Jul 05, 2013 at 13:25 UTC

    You’re just having precedence problems with the various operators. Change the second print statement to:

    print 'a' cmp 'b', "\n"; # -1

    i.e., use the comma operator instead of the concatenation operator. And repeat for the other print statements. You should then get the results you’re expecting.

    Update: To clarify: the concatenation operator . has a higher precedence than the stringwise comparison operator cmp, so your second print statement is parsed as print 'a' cmp ( 'b' . "\n" ); But the comma operator , has lower precedence, so it parses as you expect. See Operator Precedence and Associativity.

    Hope that helps,

    Athanasius <°(((><contra mundum Iustus alius egestas vitae, eros Piratica,

Re: Newbie flabbergasted by string compare results
by Happy-the-monk (Canon) on Jul 05, 2013 at 13:31 UTC

    What did I do wrong here?

    Hmm, to start with, having a sub main in your script is a bit unusual, since the main routine is the one not in any subroutine with Perl.

    The dot (.) joining your comparison and the "\n" at the end of the lines to be printed does not stringify the result the way you expected it to. Use a comma (,) as a list operator instead and things will turn out to be just as you imagined.

    Cheers, Sören

    (hooked on the Perl Programming language)

Re: Newbie flabbergasted by string compare results
by tobyink (Canon) on Jul 05, 2013 at 19:41 UTC
    print "a" eq "a"."\n";

    is equivalent to:

    print("a" eq "a\n");

    You could use parentheses:

    print(("a" eq "a")."\n");

    Or use comma instead of concatenation:

    print "a" eq "a", "\n";

    Better yet, if you've got a vaguely recent version of Perl, use say:

    use feature 'say'; say "a" eq "a";
    package Cow { use Moo; has name => (is => 'lazy', default => sub { 'Mooington' }) } say Cow->new->name
Re: Newbie flabbergasted by string compare results
by rjt (Curate) on Jul 05, 2013 at 14:02 UTC
    Now I'm all flabbergasted. What's going on there? As it doesn't print the '0's, it seems the only thing that gives the right number of '1's is "Less than or equal to". Everything else seems to do something unexpected. What did I do wrong here?

    The first answers correctly note the precedence issues. Your script does not print '0's because the boolean operators (i.e., everything except for cmp) are returning '' for the boolean false value.

    To get around both issues, and learn about eval, try this instead:

    #!/usr/bin/env perl use 5.012; use warnings; use strict; my @perm = ( [qw/a b/], [qw/b a/], [qw/a a/] ); for my $op (qw/cmp eq ne lt le gt ge/) { for my $expr (map { "'$_->[0]' $op '$_->[1]'" } @perm) { printf "%s == %s\n", $expr, eval($expr) eq '' ? "''" : eval($e +xpr); } }

    Yes, I do the eval twice; normally I would avoid that, but here there is no side effect by design and the penalty is small.

    Update: Add missing { back in. (Thanks Athanasius!)

Re: Newbie flabbergasted by string compare results
by AnomalousMonk (Archbishop) on Jul 05, 2013 at 23:43 UTC

    An interesting question (and a potential breadcrumb-path to enlightenment) would have been to ask what was happening to all those newlines. Three print statements in a row with cmp, each with a newline, and the output is "-11-1". Wha...?!?

    In any event, using the O and B::Deparse modules allows you to see what Perl thinks about the code you have written.

    >perl -wMstrict -MO=Deparse,-p -e "my $c = shift @ARGV; ;; sub main() { print qq{cmp Compare:\n}; print 'a' cmp 'b'.qq{\n}; print 'b' cmp 'a'.qq{\n}; print 'a' cmp 'a'.qq{\n}; ;; print 'a' cmp 'b'.$c; } " BEGIN { $^W = 1; } use strict 'refs'; (my $c = shift(@ARGV)); sub main () { use strict 'refs'; print("cmp Compare:\n"); print((-1)); print(1); print((-1)); print(('a' cmp ('b' . $c))); } -e syntax OK

    As you see, most of the cmp expressions don't even survive compilation, being reduced to compile-time constants (because that's essentially how they started out). Only the last print statement gives Perl something to chew on at run time, and it's possible to clearly see there the precedence of operator evaluation.

Re: Newbie flabbergasted by string compare results
by Anonymous Monk on Jul 08, 2013 at 12:26 UTC
    Thank you all for elaborating on precedence, the "." vs. "," and so on. That's a point I was really missing on.

    When I change the ."\n" to ,"\n" I get results that are actually usable. :-)

    But I didn't notice a difference between say and print, other than the need for "\n" in print. Was that it?