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

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

In my script, I use:
my $page = $cgi->param("page") or "login";

But it always uses $cgi->param("page"), even if the return value is undef. I remember using this in another program and having it work . . . what am I doing differently? Isn't this supposed to work?

Thanks.

Replies are listed 'Best First'.
Re: Or Operator
by fruiture (Curate) on Jul 30, 2004 at 23:04 UTC

    The magic term is "operator precedence", it determines the order of execution or your atomic expressions.

    A = B or C; # as "=" has higher precedence than "or" it binds it's # arguments "tighter", so this is equivalent to (A = B) or C; # so your expression is a disjunction # which executes the left operand (A = B) # being an assignment, which returns B in the end # and if that's false, the disjunction (or) # returns C, which is irrelevant, because that's in void # context # but you want A = (B or C) # because that's an assignment, an assignment # evaluates the right operand first (B or C) # which itself is a disjunction again and works as described # above, returning B if true, otherwise C # and that is then assigned to A, which must be an lvalue # that means in Perl my $page = ( $cgi->param("page") or "login" ); # or by using the high-precedence-OR "||" my $page = $cgi->param("page") || "login"; # because "||" has higher precedence than "="

    see perlop

    --
    http://fruiture.de
      I have another similar line:
      $ftp = Net::FTP->new("ftp.$dom", Debug => 0) or $loginerror = "Error message.";

      Should this work like I think it does?

        If you think it will set $ftp to the result of the new method call, and if that was false set $longin error, it will.
Re: Or Operator
by thor (Priest) on Jul 31, 2004 at 14:27 UTC
    As was stated above, you're having problems with operator precedence. Consider the following two one-liners:
    perl -le '$a = undef or 1; print $a' perl -le '$a = undef || 1; print $a'
    You may have to change the single-quotes to double-quotes to get the above examples to run from the command line, depending on what platform you're on

    As perlop tells us, 'or' has lower precedence than '=', while '||' has higher precedence than '='. So the first one-liner is equivalent to:

    perl -le '($a = undef) or "foo"; print $a'
    whereas the second one is equivalent to:
    perl -le '$a = (undef or "foo"); print $a'

    thor

    Feel the white light, the light within
    Be your own disciple, fan the sparks of will
    With all of us waiting, your kingdom will come

Re: Or Operator
by VSarkiss (Monsignor) on Jul 31, 2004 at 16:46 UTC

    The answers above address your question directly. If you ever need to figure this out on your own, the B::Deparse module, which comes standard with every Perl distribution, can be very helpful:

    $ perl -MO=Deparse -e 'my $foo = bar() or baz()' baz() unless my $foo = bar(); -e syntax OK $ perl -MO=Deparse -e 'my $foo = bar() || baz()' my $foo = bar() || baz(); -e syntax OK
    You can see how the two are interpreted very differently.

Re: Or Operator
by rjahrman (Scribe) on Jul 30, 2004 at 22:53 UTC
    (It's me again.)

    BTW, I got it to work with:
    $page = $cgi->param("page") or $page = "login";

    But this:
    my $page = $cgi->param("page") or $page = "login";
    Fails. Why? Is there something I can change to make it work?

      my $page = $cgi->param("page") or $page = "login";
      fails because that second $page is really the global variable; the scope where "$page" refers to the my variable only starts on the following statement. use strict would have alerted you to this (unless you have an outer $page, which is not a good idea for keeping your code maintainable.)
Re: Or Operator
by Avitar (Acolyte) on Jul 30, 2004 at 23:15 UTC
    my $page = $cgi->param("page") Or "login";

    should prob be rewritten as a conditional statement for clairy...

    my($page) = $cgi->param("page"); if($page eq ""){ $page = "login";}


    Try it with a lowercase 'or'; Remember if it is worth writing, it is worth writing clear... if possible even difficult code should be understandable by even the most unexperianced.
      I think this is the clearest and most concise way to express it: my $page = $cgi->param("page") || "login"; You might say that inexperienced people won't understand it, but once they see it a few hundred times (because this is a very common way of doing it), they'll probably get it.
        Then again, if there is a chance that $cgi->param("page") will be 0 - and you want to accept that, || will not work correctly. We'll get // (defined-or) in Perl 5.10, fortunately.

      Except that will give you warnings if the page parameter is actually non-existent.

      You want to test definedness as well as emptiness.

      my $page = $cgi->param( "page" ); unless( defined $page and $page ne "" ){ $page = "login"; }

      Of course that's pretty clusmy and contains lots of unnecessary punctuation. Let's use a statement modifier instead:

      my $page = $cgi->param("page"); $page = "login" unless defined $page and $page ne "";

      Makeshifts last the longest.