What does this do ? !/somestring/

A

Abhinav

Hi,

I have a script :

#!/usr/bin/perl
use strict;
use warnings;

my $str="asd";
$str =~ !/asd/; # ! should not be there
print $str . "\n";

While running the above, I get the output

Use of uninitialized value in pattern match (m//) at t1.pl line 5.
asd
$

What does the ! do? How is it allowed at all ?

The question came up since someone was incorrectly using
$string =~ !// instead of
$string !~ //

That is worked in some cases (obviously with warnings/strictures off)
puzzled me. Hence this question

Thanks
Abhinav
 
J

J. Romano

Abhinav said:
I have a script :

#!/usr/bin/perl
use strict;
use warnings;

my $str="asd";
$str =~ !/asd/; # ! should not be there
print $str . "\n";

While running the above, I get the output

Use of uninitialized value in pattern match (m//) at t1.pl line 5.


You get that warning because the line:

$str =~ !/asd/;

is equivalent to:

$str =~ ! ($_ =~ /asd/);

and also equivalent to:

$str =~ ($_ !~ /asd/);

which is probably not what you want. But since $_ probably has not
been set yet, you receive the warning message about a value (in this
case, $_) not being initialized.

The original programmer probably got confused because he/she had
read conditions like:

if (!/asd/)
{
# do something...
}

In this case, the condition:

!/asd/

is equivalent to:

! ($_ =~ m/asd/)

and also to:

$_ !~ m/asd/

The condition without the '!', like this:

/asd/

is equivalent to:

$_ =~ m/asd/

which may have let the programmer to believe that adding a '!'
(instead of an 'm') in front of the regular expression automatically
negates the match. It can negate the match, but only when the "$_ =~"
part is implied. The programmers's mistake was in thinking that '!'
negates the match everywhere.

I hope this helps clear things up, Abhinav.

-- Jean-Luc
 
A

Abhinav

J. Romano said:
You get that warning because the line:

$str =~ !/asd/;

is equivalent to:

$str =~ ! ($_ =~ /asd/);

and also equivalent to:

$str =~ ($_ !~ /asd/);

Hmm ..
Reading this inside out, this would

1. check if $_ !~ /asd/
2. The return value from this check is taken an expression, which is on the
right side of $str

Hence, $str =~ expr # This is evaluated as $str =~ /expr/

This is what I could ascertain from "Programming Perl", page 90, "Binding
Operators". I hope I am correct ?
which is probably not what you want. But since $_ probably has not
been set yet, you receive the warning message about a value (in this
case, $_) not being initialized.

The original programmer probably got confused because he/she had
read conditions like:

if (!/asd/)
{
# do something...
}

In this case, the condition:

!/asd/

is equivalent to:

! ($_ =~ m/asd/)

and also to:

$_ !~ m/asd/

The condition without the '!', like this:

/asd/

is equivalent to:

$_ =~ m/asd/

which may have let the programmer to believe that adding a '!'
(instead of an 'm') in front of the regular expression automatically
negates the match. It can negate the match, but only when the "$_ =~"
part is implied. The programmers's mistake was in thinking that '!'
negates the match everywhere.

I hope this helps clear things up, Abhinav.

Definitely ! Thanks a lot for your detailed explanation :)

Regards
Abhinav
 
P

Paul Lalli

J. Romano said:
Abhinav <[email protected]> wrote in message

You get that warning because the line:

$str =~ !/asd/;

is equivalent to:

$str =~ ! ($_ =~ /asd/);

and also equivalent to:

$str =~ ($_ !~ /asd/);

This didn't make a lot of sense to me until I ran it through the Deparse
functionality of perl. It may help others to understand it if they see
the intermediary step here:

perl -MO=Deparse -e'$str ="asd"; $str =~ !/asd/;'

$str = 'asd';
$str =~ not /asd/;
-e syntax OK

So in the above, $str is being pattern matched against the result of
(not /asd/), which, as J. Romano correctly points out, is the equivalent
of
not ($_ =~ /asd/)
or
$_ !~ /asd

Paul Lalli
 
J

J. Romano

Abhinav said:
Hmm ..
Reading this inside out, this would

1. check if $_ !~ /asd/
2. The return value from this check is taken an expression,
which is on the right side of $str

Hence, $str =~ expr # This is evaluated as $str =~ /expr/

This is what I could ascertain from "Programming Perl",
page 90, "Binding Operators". I hope I am correct ?


It looks like you are correct, Abhinav. According to the "Binding
Operators" section of "perldoc perlop", "if the right argument is an
expression rather than a search pattern [(or a substitution or
transliteration)] it is interpreted as a search pattern at run time."
So the code:

$str =~ "asd";

is just a sloppy way of saying:

$str =~ m/asd/;

It gets weirder, however. According to "perldoc perlop" in the
section "m/PATTERN/cgimosx", "if the PATTERN evaluates to the empty
string, the last
*successfully* matched regular expression is used instead."

Usually this isn't a major concern, but it is if the pattern
contains a variable that is set to the empty string. So how does that
affect you?

Well, by tinkering around with Perl's regular expression matching,
it seems that m/PATTERN/ either returns a 1 if the match was
successful, or "" (the empty string -- which evaluates to false) if
the match was unsuccessful. Putting a "!" in front of either will
return the other (in other words, ((!1) eq "") and ((!"") eq 1).

Therefore, the original line of code you posted:

$str =~ !/asd/;

is equivalent to:

$str =~ ! ($_ =~ m/asd/);

If $_ is either undefined or does not contain the string "asd",
then the line is also equivalent to:

$str =~ ! ("");

which is equivalent to:

$str =~ 1;

which is equivalent to:

$str =~ m/1/;

which will return true only if $str contains a "1".

On the other hand, if $_ is defined and contains the string "asd",
then the line:

$str =~ !/asd/;

which, as I already said, is equivalent to:

$str =~ ! ($_ =~ m/asd/);

is now equivalent to:

$str =~ ! (1);

which is equivalent to:

$str =~ "";

which is equivalent to:

$str =~ m//;

which, according to the perldoc, will not try to match an empty
string, but will use the last *successfully* matched regular
expression instead.

Therefore, if you see had lines of code:

"Wizard of Wor" =~ m/Wor/; # successful match
"All your base are belong to us" =~ m/bases/;
$_ = "wasderful";
$str = "Hello, World!";
$str =~ !/asd/; # original line

the line:

$str =~ !/asd/; # original line

will be equivalent to:

$str =~ m/Wor/;

because the m/Wor/ match was the last successful pattern match. It
will therefore evaluate to true, even though $str does not contain the
string "asd".

Strange, isn't it?

Maybe this bizzare side-effect helps explain why, as you said in
your first post, that the code worked in some cases. The original
line of code was meant to check for the non-existence of "asd", but
instead checked for the existence of "Wor". Either way, it's still
possible to evaluate to true in some cases (like the one I just showed
you).

I hope this explains things more than it confused you. To tell you
the truth, Abhinav, your question made me search through the Perl
documentation for a good while before I figured out what was
happening. In other words, a lot more was happening "behind the
scenes" with your code than I first realized!

Cheers,

Jean-Luc
 
M

Malcolm Dew-Jones

J. Romano ([email protected]) wrote:

: which is equivalent to:

: $str =~ m//;

: which, according to the perldoc, will not try to match an empty
: string, but will use the last *successfully* matched regular
: expression instead.

It's documented, but it's a trap, and not mentioned in perltraps.pod as
far as I can see. Perhaps this gotcha is mention in the faqs, but not
that I saw.

'use warnings' will warn you if the $var is undefined, but if it's defined
but length = 0 then you get no warning about the probably unexpected m//
behaviour.

The hard part is to remember to protect yourself in this situation.

The solution depends on the correct interpretation of m// at that point in
your program. Technically m// should match everything, but that may or may
not make sense in a particular situation.

if matching everything is fine then one trick is to say

m/^/; # always matches, and isn't empty

if ( m/$var/ ) # now forced to match if $var is empty

or you can be explicit

if ( $var eq '' or m/$var/ )

or the reverse if appropriate

if ( $var ne '' and m/$var/ )

or there's always
die unless defined $var and $var ne ''

but like I said, the hard part is to remember you need to do something at
all.
 
A

Abhinav

J. Romano said:
Abhinav said:
Hmm ..
Reading this inside out, this would

1. check if $_ !~ /asd/
2. The return value from this check is taken an expression,
which is on the right side of $str

Hence, $str =~ expr # This is evaluated as $str =~ /expr/

This is what I could ascertain from "Programming Perl",
page 90, "Binding Operators". I hope I am correct ?



It looks like you are correct, Abhinav. According to the "Binding
Operators" section of "perldoc perlop", "if the right argument is an
expression rather than a search pattern [(or a substitution or
transliteration)] it is interpreted as a search pattern at run time."
So the code:

$str =~ "asd";

is just a sloppy way of saying:

$str =~ m/asd/;

It gets weirder, however. According to "perldoc perlop" in the
section "m/PATTERN/cgimosx", "if the PATTERN evaluates to the empty
string, the last
*successfully* matched regular expression is used instead."

Usually this isn't a major concern, but it is if the pattern
contains a variable that is set to the empty string. So how does that
affect you?

Well, by tinkering around with Perl's regular expression matching,
it seems that m/PATTERN/ either returns a 1 if the match was
successful, or "" (the empty string -- which evaluates to false) if
the match was unsuccessful. Putting a "!" in front of either will
return the other (in other words, ((!1) eq "") and ((!"") eq 1).

Therefore, the original line of code you posted:

$str =~ !/asd/;

is equivalent to:

$str =~ ! ($_ =~ m/asd/);

If $_ is either undefined or does not contain the string "asd",
then the line is also equivalent to:

$str =~ ! ("");

which is equivalent to:

$str =~ 1;

which is equivalent to:

$str =~ m/1/;

which will return true only if $str contains a "1".

On the other hand, if $_ is defined and contains the string "asd",
then the line:

$str =~ !/asd/;

which, as I already said, is equivalent to:

$str =~ ! ($_ =~ m/asd/);

is now equivalent to:

$str =~ ! (1);

which is equivalent to:

$str =~ "";

which is equivalent to:

$str =~ m//;

which, according to the perldoc, will not try to match an empty
string, but will use the last *successfully* matched regular
expression instead.

Therefore, if you see had lines of code:

"Wizard of Wor" =~ m/Wor/; # successful match
"All your base are belong to us" =~ m/bases/;
$_ = "wasderful";
$str = "Hello, World!";
$str =~ !/asd/; # original line

the line:

$str =~ !/asd/; # original line

will be equivalent to:

$str =~ m/Wor/;

because the m/Wor/ match was the last successful pattern match. It
will therefore evaluate to true, even though $str does not contain the
string "asd".

Strange, isn't it?

Maybe this bizzare side-effect helps explain why, as you said in
your first post, that the code worked in some cases. The original
line of code was meant to check for the non-existence of "asd", but
instead checked for the existence of "Wor". Either way, it's still
possible to evaluate to true in some cases (like the one I just showed
you).

I hope this explains things more than it confused you. To tell you
the truth, Abhinav, your question made me search through the Perl
documentation for a good while before I figured out what was
happening. In other words, a lot more was happening "behind the
scenes" with your code than I first realized!

Thank you for your detailed investigation and lucid analysis..they were a
great help.

As I had mentioned earlier, this does not affect my code...someone had been
using the !// construct incorrectly, and getting by with it for no apparent
reason. Even Perl did not complain much.. :)

Your post explains it to a point of being crystal clear :)

Thanks,

Abhinav

[Not removing the text below so that the archive has a complete explanation..]
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,780
Messages
2,569,611
Members
45,276
Latest member
Sawatmakal

Latest Threads

Top