From: jl_post on

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
From: C.DeRykus on
On Jul 23, 9:00 am, "jl_p...(a)hotmail.com" <jl_p...(a)hotmail.com> wrote:
> 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.
>

Wouldn't the short-circuiting List::Util::first or
List::MoreUtils::any fit the bill then...?


if ( List::Util::first { looks_like_number($_) } @a ) {...}

if ( List::MoreUtils::any { looks_like_number($_) } @a {...}


>
>    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)?
>

Maybe something such as this:

use feature 'state';

if ( @a ~~ sub{ state $r; $r //=
L::U::first {looks_like_number($_)} @a
} )
....


--
Charles DeRykus
From: jl_post on
> On Jul 23, 9:00 am, "jl_p...(a)hotmail.com" <jl_p...(a)hotmail.com> wrote:
>
> > but I'd rather not write a new subroutine every time I need the any()
> > behavior.

On Jul 23, 3:00 pm, "C.DeRykus" <dery...(a)gmail.com> replied:
>
> Wouldn't the short-circuiting List::Util::first or
> List::MoreUtils::any fit the bill then...?
>
> if ( List::Util::first { looks_like_number($_) } @a ) {...}
>
> if ( List::MoreUtils::any { looks_like_number($_) } @a {...}


Ah, List::MoreUtils is exactly what I'm looking for. Thanks.
Unfortunately, List::MoreUtils doesn't seem to be a standard module
(at least according to http://perldoc.perl.org/index-modules-L.html).

So on platforms where I can only depend on standard modules I
suppose I can use List::Util::first() instead, as long as the set of
values I'm looking for can never be undefined. (Otherwise, I wouldn't
know if I got a proper match, or if no match at all was found.)

(So now I know a nice work-around to getting "Array ~~ CodeRef"
any() behavior, but I still don't know the reason behind the "Array ~~
Regex" and "Array ~~ CodeRef" any/all discrepancy.)

Thanks again for the response, Charles. It was useful.

Cheers,

-- Jean-Luc
From: Alan Curry on
In article <a4526931-df5f-4acd-9b48-66e5143f9a09(a)i31g2000yqm.googlegroups.com>,
jl_post(a)hotmail.com <jl_post(a)hotmail.com> wrote:
>
> 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,

No it doesn't.

Try it with @a=(1,2,3); it's still false.

The coderef is only called once, with \@a as the argument. You passed an
arrayref to looks_like_number, which is going to be false no matter what.

You're right about one thing at least: it's not easy to predict what ~~ will
do based on the documentation.

--
Alan Curry
From: Ilya Zakharevich on
On 2010-07-23, jl_post(a)hotmail.com <jl_post(a)hotmail.com> wrote:
> I find this a bit counter-intuitive.

There is nothing "a bit counter-intuitive" about the smart matching
operation. It is just *absolutely useless*, since there is no
humanly-possible way to predict what it would do.

The only way to handle the situation is to ignore this operator
completely, and black-list as unmaintainable any code which uses it.

Hope this helps,
Ilya