J
jl_post
Hi,
I've been reading up on the smart matching operator ('~~') in
"perldoc perlsyn", and I have to say I'm a little confused on a
certain aspect of it.
Say I have an array like:
my @a = ('cat', 'dog', 55);
If I want to discover if any of its elements contains only digits,
I can use the '~~' operator against a regular expression, like this:
if ( @a ~~ m/^\d+$/ ) # prints "true"
{
print "true";
}
else
{
print "false"
}
In this respect, the smart matching operator behaves like an "any()"
function, returning a true value if any element matches.
But if I wanted to discover if any of its elements looks like a
number with the Scalar::Util::looks_like_number() function in a code
reference, like this:
use Scalar::Util qw(looks_like_number);
if ( @a ~~ sub { looks_like_number($_[0]) } ) # prints "false"
{
print "true";
}
else
{
print "false"
}
the smart matching operator will return a false value. In this case,
the smart matching operator behaves like an "all()" function,
returning a true value only if every element returns true (or there
are no elements that return false).
So "Array ~~ Regex" returns true in any elements evaluate to true,
but "Array ~~ CodeRef" returns true only if all elements evaluate to
true.
I find this a bit counter-intuitive. Is there any logic behind
this behavior? (I'm asking because I find it difficult to remember
which one behaves like any() and which one behaves like all(), since
the distinction seems arbitrary to me.)
I've discovered that if I want "Array ~~ Regex" to behave like an
all() function, I can basically rewrite it by wrapping it in a
CodeRef, like this:
if ( @a ~~ sub { $_[0] =~ m/^\d+$/ } ) # evaluates to false
However, if I want "Array ~~ CodeRef" to behave like an any()
function, I have to resort to using De Morgan's laws and write it like
this:
if (not( @a ~~ sub { not looks_like_number($_[0]) } )) # evaluates
to true
This works, but I find that this makes the code confusing to read (and
to maintain).
Is there a better way to get "Array ~~ CodeRef" to behave like an
any() function? (Without abandoning the '~~' operator, that is.) I'm
aware that I can do this:
if ( grep { looks_like_number($_) } @a ) # evaluates to true
but I know from experience that many seasoned Perl coders are against
this approach. I also know of this approach documented in "perldoc
List::Util :
sub any { looks_like_number($_) && return 1 for @_; 0 }
if ( any(@a) ) # evaluates to true
but I'd rather not write a new subroutine every time I need the any()
behavior.
So basically I'm asking two things:
1.) Why does "Array ~~ Regex" act like any() whereas "Array ~~
CodeRef" act like all()? (In other words, why the discrepancy?)
and:
2.) Is there a simple/elegant way to get "Array ~~ CodeRef" to behave
like an any() function (like "Array ~~ Regex" does)?
Thanks in advance for any advice.
-- Jean-Luc
I've been reading up on the smart matching operator ('~~') in
"perldoc perlsyn", and I have to say I'm a little confused on a
certain aspect of it.
Say I have an array like:
my @a = ('cat', 'dog', 55);
If I want to discover if any of its elements contains only digits,
I can use the '~~' operator against a regular expression, like this:
if ( @a ~~ m/^\d+$/ ) # prints "true"
{
print "true";
}
else
{
print "false"
}
In this respect, the smart matching operator behaves like an "any()"
function, returning a true value if any element matches.
But if I wanted to discover if any of its elements looks like a
number with the Scalar::Util::looks_like_number() function in a code
reference, like this:
use Scalar::Util qw(looks_like_number);
if ( @a ~~ sub { looks_like_number($_[0]) } ) # prints "false"
{
print "true";
}
else
{
print "false"
}
the smart matching operator will return a false value. In this case,
the smart matching operator behaves like an "all()" function,
returning a true value only if every element returns true (or there
are no elements that return false).
So "Array ~~ Regex" returns true in any elements evaluate to true,
but "Array ~~ CodeRef" returns true only if all elements evaluate to
true.
I find this a bit counter-intuitive. Is there any logic behind
this behavior? (I'm asking because I find it difficult to remember
which one behaves like any() and which one behaves like all(), since
the distinction seems arbitrary to me.)
I've discovered that if I want "Array ~~ Regex" to behave like an
all() function, I can basically rewrite it by wrapping it in a
CodeRef, like this:
if ( @a ~~ sub { $_[0] =~ m/^\d+$/ } ) # evaluates to false
However, if I want "Array ~~ CodeRef" to behave like an any()
function, I have to resort to using De Morgan's laws and write it like
this:
if (not( @a ~~ sub { not looks_like_number($_[0]) } )) # evaluates
to true
This works, but I find that this makes the code confusing to read (and
to maintain).
Is there a better way to get "Array ~~ CodeRef" to behave like an
any() function? (Without abandoning the '~~' operator, that is.) I'm
aware that I can do this:
if ( grep { looks_like_number($_) } @a ) # evaluates to true
but I know from experience that many seasoned Perl coders are against
this approach. I also know of this approach documented in "perldoc
List::Util :
sub any { looks_like_number($_) && return 1 for @_; 0 }
if ( any(@a) ) # evaluates to true
but I'd rather not write a new subroutine every time I need the any()
behavior.
So basically I'm asking two things:
1.) Why does "Array ~~ Regex" act like any() whereas "Array ~~
CodeRef" act like all()? (In other words, why the discrepancy?)
and:
2.) Is there a simple/elegant way to get "Array ~~ CodeRef" to behave
like an any() function (like "Array ~~ Regex" does)?
Thanks in advance for any advice.
-- Jean-Luc