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

At the end of List::Util's POD is a list of functions that the author, the ever-prolific Graham Barr, has decided are too trivial to include. The first example is:

# One argument is true sub any { $_ && return 1 for @_; 0 }
I don't see that quite as useful as:
sub any(&@) { my $code = shift; $code->() && return 1 for @_; 0 }
The reason is that the original means you have to map your list to true/false values before using any while the latter means you can evaluate each one until you find the one you're interested in.

The observant may notice that this is exactly the same as first - but the extremely observant will notice that it is merely almost the same as first. To whit:

my $has_undefs = first { not defined } @list;
This will set $has_undefs to undef no matter what. However, substitute first for any (my version of any, not Graham's), and now it will return true if any element of @list is undef.

Similar changes could be made to all, notall, and none. The true and false (counting) functions, however, I don't see such a change required simply because the underlying code is already generic. Having a function named "count" like this:

sub count(&@) { my $code = shift; scalar grep { $code->() } @_ }
doesn't seem to give you much. Calling count { blah() } @list vs scalar grep { blah() } @list doesn't seem significant.

Since I've noticed Graham's contributions to perl are somewhat larger than my own, I'm going to make the assumption that I'm missing something, not him. Thus, I'd appreciate if anyone could enlighten me as to what I'm missing. ;-)

PS - I've used this code in Template::Plugin::ByDate which is my own answer to a previous post here. So, if I'm missing something, the sooner I know, the better. :-)

Update: As borisz points out, List::MoreUtils has what I was looking for. I had completely forgotten about that. Thanks, borisz! I've installed that and updated my code to use it.

Replies are listed 'Best First'.
Re: List::Util's any function
by borisz (Canon) on Apr 25, 2006 at 17:47 UTC
    Just use List::MoreUtils where all the functions you like to change are already work the way you want and they are in XS.
    Boris
Re: List::Util's any function
by diotalevi (Canon) on Apr 25, 2006 at 16:02 UTC

    It's quite a bit cheaper to avoid calling a function. This is just a case where you use heavier machinery if you need it and don't if you don't.

    ⠤⠤ ⠙⠊⠕⠞⠁⠇⠑⠧⠊

      Actualy i think you are wrong. ;) Unless my bechmark is way off (which if could be). Any that takes a sub beats the original any and grep hands down everytime. By a rather large margin in fact, and the margin grows substantialy with the size of the data.

      use strict; use warnings; use Benchmark qw/cmpthese/; sub any_1 { $_ && return 1 for @_; 0 } sub any_2 { my $code = shift; $code->($_) && return 1 for @_; 0; } my @data; push @data, int rand 50 for 1..100; my @large_data; push @large_data, int rand 50 for 1..10_000; cmpthese(-5, { any_1_small => sub { any_1( map { $_ > 20 } @data); }, any_2_small => sub { any_2( sub { $_ > 20 }, @data); }, grep_small => sub { scalar grep{ $_ > 20 } @data; }, }); cmpthese(-5, { any_1_large => sub { any_1( map { $_ > 20 } @large_data); }, any_2_large => sub { any_2( sub { $_ > 20 }, @large_data); }, grep_large => sub { scalar grep{ $_ > 20 } @large_data; }, }); __END__ Rate any_1_small grep_small any_2_small any_1_small 8962/s -- -84% -96% grep_small 56517/s 531% -- -73% any_2_small 209808/s 2241% 271% -- Rate any_1_large grep_large any_2_large any_1_large 57.3/s -- -90% -99% grep_large 550/s 860% -- -89% any_2_large 5052/s 8723% 819% --

      Perhaps I missed your point but it definitly seems that calling a function is better in this case. That is probably because it short circuits quite soon and avoids alot of unneeded workd./me goes to run some tests for when any would fail.

      UPDATE:

      I benchmarked some more for less matches and no matches.

      use strict; use warnings; use Benchmark qw/cmpthese/; sub any_1 { $_ && return 1 for @_; 0 } sub any_2 { my $code = shift; $code->($_) && return 1 for @_; 0; } my @data; push @data, int rand 50 for 1..1000; cmpthese(-5, { any_1_20 => sub { any_1( map { $_ > 20 } @data); }, any_2_20 => sub { any_2( sub { $_ > 20 }, @data); }, grep_20 => sub { scalar grep{ $_ > 20 } @data; }, }); cmpthese(-5, { any_1_45 => sub { any_1( map { $_ > 45 } @data); }, any_2_45 => sub { any_2( sub { $_ > 45 }, @data); }, grep_45 => sub { scalar grep{ $_ > 45 } @data; }, }); cmpthese(-5, { any_1_60 => sub { any_1( map { $_ > 60 } @data); }, any_2_60 => sub { any_2( sub { $_ > 60 }, @data); }, grep_60 => sub { scalar grep{ $_ > 60 } @data; }, }); __END__ Rate any_1_20 grep_20 any_2_20 any_1_20 687/s -- -88% -99% grep_20 5583/s 713% -- -92% any_2_20 72133/s 10400% 1192% -- Rate any_1_45 grep_45 any_2_45 any_1_45 682/s -- -89% -97% grep_45 6013/s 781% -- -75% any_2_45 24282/s 3459% 304% -- Rate any_2_60 any_1_60 grep_60 any_2_60 625/s -- -3% -90% any_1_60 645/s 3% -- -89% grep_60 6017/s 863% 833% --

      Grep takes a nice lead when there will be no matche, but any_2 still holds even with any_1, so at worst its only as bad as any_1 and no slower.


      ___________
      Eric Hodges