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


in reply to Smartmatch alternatives

My problem with smartmatch is that it tries to replace several more explicit constructs. I prefer to just use the more explicit one, leaving less to heuristics. grep would be fine except for one problem; it does an exhaustive search. There's no (sane) way to tell it to stop searching after the first element meets the search criteria. I don't know if smartmatch stops after the first satisfying condition.

List::Util is a core Perl module. We should consider the functions it offers to be almost as much "first class citizens" as grep. ...except that for documentation we have to look at the module's POD rather than perldoc -f. One of its functions is any. This function stops searching immediately, and returns a true value as soon as one item meets the search criteria. And its name conveys exactly what it does; it's not searching for a whole bunch of things (like grep), it's just going to tell us whether any of the elements meet the criteria.

my $found = any { $_ eq 'something' } @array_of_strings; my $found = any { /\bsomething\b/ } @array_of_strings; my $found = any { $_ == 42 } @array_of_numbers; my $found = any { $_->isa('obj_type') } @array; my $found = any { $_->can('tupitar') } @array;

It's not as succinct as $something ~~ @array, but I can look at it and immediately know the semantics under which $something is being tested, whereas with smartmatch I have to open up perlop and check that my intuition isn't playing tricks on me.


Dave

Replies are listed 'Best First'.
Re^2: Smartmatch alternatives
by tobyink (Canon) on Dec 17, 2013 at 19:22 UTC

    I agree with:

    "My problem with smartmatch is that it tries to replace several more explicit constructs. I prefer to just use the more explicit one, leaving less to heuristics."

    ... in most cases. Don't use ~~ when ==, eq or =~ would do a better job.

    However, there is one place I really miss smartmatch. Let's take for example RDF::RDB2RDF; you don't need to know all the details of what it does; suffice to say that it's got user-configurable objects which process a database. One of the options that can be configured is which tables (if any) should be skipped. I liked being able to say "set the ignore_tables option to anything that can be used as the right hand side of a smart match".

    So if they wanted to skip one particular table, they could do ignore_tables => "foo", if they had a list of tables to skip, then ignore_tables => \@private, or they could use a regexp ignore_tables => qr{^priv}, or if they had more complex requirements, ignore_tables => sub {...}.

    Smartmatch made implementing that really easy. And it's that aspect of smart match that made me want to write a stable replacement for it.

    use Moops; class Cow :rw { has name => (default => 'Ermintrude') }; say Cow->new->name
Re^2: Smartmatch alternatives
by LanX (Saint) on Dec 17, 2013 at 15:56 UTC
    Agreed, but any is in List::MoreUtils (which isn't core. =)

    And XS and it's alternative pure Perl implementation was buggy last time I looked into it.

    update

    oh did you mean first ? :)

    use List::Util qw(first max maxstr min minstr reduce shuffl +e sum); first BLOCK LIST ... "first" returns the first element where the result from BLOCK is a true value.

    update

    grr @%$§ !!!...

    DB<121> @a=0..10 => (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10) DB<122> first { "0" eq $_ } @a => 0

    Cheers Rolf

    ( addicted to the Perl Programming Language)

      No, I meant List::Util::any.

      $ perl -MList::Util -E 'say "Yes" if List::Util::any { $_ == 42 } @{[q +w/1 3 42 5/]}' Yes

      It shows up in both modules. There may be subtle, undocumented differences.


      Dave

        I see that it was added after 1.32 (most recent version installed here) ...

        1.33 -- Sun Oct 13 01:35 UTC 2013 * Added any, all, none, notall list reduction functions (inspired by List::MoreUtils)
        OK your CPAN- version doesn't show it in the synopsis

        SYNOPSIS use List::Util qw(first max maxstr min minstr reduce shuffle sum);

        and my 5.10 version does only list it as suggested.

        Anyway good news! (for me at least =)

        Cheers Rolf

        ( addicted to the Perl Programming Language)

      Well, L'U'first returns undef "[li]f BLOCK never returns true or LIST was empty" (as of version 1.32), so need to do check further if value is defined.

        so this should be a fast and core fallback if no better module available.

        DB<125> @a=(0)x10 => (0, 0, 0, 0, 0, 0, 0, 0, 0, 0) DB<126> sub any (&@) { my $cr=shift; defined first { &$cr } @_ } DB<127> any {$_==0} @a => 1

        well could be faster if I could avoid '$cr' but my golfing foo is weak today. =)

        update

        got it :)

        DB<138> sub any (&@) { defined &first (shift, @_) } DB<139> any {$_==0} @a => 1

        update

        better :)

        DB<144> sub any (&\@) { defined &first } DB<145> any {$_==0} @a => 1

        UPDATE

        DARN! still buggy with false positives!

        didn't sleep enough...

        update

        this seems to work...

        DB<230> sub any (&@) { my $x=&List::Util::first; defined $x } DB<231> any { $_ eq 0 } @a => "" DB<232> any { $_ eq 5 } @a => 1 DB<233> @a => (1, 2, 3, 4, 5)

        Cheers Rolf

        ( addicted to the Perl Programming Language)