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

Hello monks,

At DB<2> below the result is '1' instead of the 'b' I expected.
It seems like the regexp is getting a scalar context for some reason.

What is going on?

DB<1> ($a) = ("abc" =~ /a(b)c/); print $a b DB<2> ($a) = (("abc" =~ /a(b)c/) || 'd'); print $a 1 DB<3>($a) = (("ac" =~ /a(b)c/) || ('d')); print $a d

Replies are listed 'Best First'.
Re: Regexp context with ||
by hv (Prior) on Apr 09, 2003 at 02:00 UTC

    || always imposes a scalar context on its left hand operand, which is also why you can't usefully say @a = @b || @c.

    One way to work around that in this case is to use the ternary operator instead:

    $a = ("ac" =~ /a(b)c/) ? $1 : 'd'; print $a;

    Update: Another approach is to use an array dereference, which is a way of retaining the ||, though I think it is less clear:

    $a = ("abc" =~ /a(b)c/)[0] || 'd'; print $a;

    Hugo
      I see, thanks.

      In perlop I should've read

      @a = @b || @c; # this is wrong @a = scalar(@b) || @c; # really meant this
      instead of misinterpreting
      Scalar or list context propagates down to the right operand if it is evaluated.

      Personally I prefer the (TEST)?TRUE:FALSE method (C many many years ago) but extending the idea of using a list (but not clarity):-

      ($a,$throw_away) = (("abc" =~ /a(b)c/) , 'd');

      if the RegEx doesn't match then you have a single element list containing 'd', if it does then 1st element is still 'b' the $throw_away variable just captures any remaining elements.

      or
      $a = ( ("abc" =~ /a(b)c/) , 'd' )[0];

      Taking this idea to an extreme, what about pulling set of values from the regex or setting all the variables to a set value.

      ($a,$b,$throw_away) = (("abcde" =~ /a(b)c(d)/),('UNDEFINED') x 3); print $a , "::" , $b ; # prints b::d and ($a,$b,$throw_away) = (("abcde" =~ /a(x)c(x)/),('UNDEFINED') x 3); print $a , "::" , $b ; # prints UNDEFINED::UNDEFINED
Re: Regexp context with ||
by tachyon (Chancellor) on Apr 09, 2003 at 02:50 UTC

    In that vein you can do this:

    $q = new CGI; my $val = $q->param('val') || 'default'; # but this does not work as you might expect my @vals = $q->param('val') || ('default');

    cheers

    tachyon

    s&&rsenoyhcatreve&&&s&n.+t&"$'$`$\"$\&"&ee&&y&srve&&d&&print

Re: Regexp context with ||
by graff (Chancellor) on Apr 09, 2003 at 02:08 UTC
    Yes, somehow it does seem unavoidable that in the second case, the regex match gets coerced to return a scalar. I guess the only way to get the desired behavior on one line would be:
    $a = ( "abc" =~ /a(b)c/ ) ? $1 : 'd';
    which I would tend to prefer anyway, even if your second case worked the way you wanted it to.
      Why not use or, and, eq, ne, lt, gt, le, ge for string operations instead?
      ($a) = "abc" =~ /a(b)c/ || 'd'; print $a; # prints 1 ($a) = "abc" =~ /a(b)c/ or 'd'; print $a; # prints 'b'
      Update: Added code that didn't post
        You might be surprised by this though:
        ($a) = "abc" =~ /a(b)c/ or 'd'; print defined $a ? "defined as $a\n" : "undef\n"; # prints "defined as + b" ($a) = "abc" =~ /a(x)c/ or 'd'; print defined $a ? "defined as $a\n" : "undef\n"; # prints "undef"
        See, your or is lower than the assignment. The assignment is happening at all times, assigning undef if the match fails.

        So, that isn't the answer either.

        Perhaps what you're looking for is:

        ($a) = "abc" =~ /a(b)c/ or $a = "d";

        -- Randal L. Schwartz, Perl hacker
        Be sure to read my standard disclaimer if this is a reply.