f



Lisp's "some" and "every" functions

Dear Perl community,

   Recently I've been learning some Lisp on my own, and I've come
across two Lisp functions that I wish Perl had:  "some" and "every".

   They work somewhat like "grep" and "map" in that they operate on a
list given a function, but they return only a boolean value.

   For example, in Lisp, to find out if at least one number in a list
is an odd number, you could write:

      (some #'oddp '(1 2 3 4 5))

or even:

      (some (lambda (x) (= 1 (mod x 2))) '(1 2 3 4 5))

   In Perl, you COULD write:

      if ( grep { $_ % 2 == 1 } (1, 2, 3, 4, 5) )
      {
         print "An odd number exists in the given list.\n";
      }

but according to 'perldoc -q "element is contained"', use of "grep" is
undesirable (especially on large lists) because "grep" checks every
element even if the first matches.

   As for the "every" function, you can use it to find out if every
element passes a certain test.  For example, in Lisp you could use it
like this to see if every element in a list is an even number:

      (every #'evenp '(2 4 5 6 8))

or even:

      (every (lambda (x) (= 0 (mod x 2))) '(2 4 5 6 8))

   In Perl, you COULD write:

      my @list = (2, 4, 5, 6, 8);

      if ( @list == grep { $_ % 2 == 0 } @list )
      {
         print "All number in given list are even.\n";
      }

but, of course, this suffers from the same inefficiency as the
previous Perl example.

   This inefficiency is not worth worrying about if the list to
examine has only a few elements, but if it has a large number of
elements (or the number of elements is arbitrary), there exist a few
work-arounds.

   One work-around is mentioned in 'perldoc -q "element is
contained"', so I won't discuss it here.  Another work-around is to
create a module that defines the "some" and "every" functions to be
used similarly to how Perl's "grep" and "map" functions are called. 
I've written the code, and here it is (more discussion follows):


use strict;
use warnings;

sub some (&@)
{
   my $sub = shift(@_);

   foreach (@_)
   {
      return 1  if &$sub;
   }

   return 0;
}

sub every (&@)
{
   my $sub = shift(@_);

   foreach (@_)
   {
      return 0  if not &$sub;
   }

   return 1;
}

1;

__END__


   Now, when this module is "use"d, you can now check for the
existence of an odd number with the "some" function like:

      my @list = (2, 4, 5, 6, 8);

      if ( some { $_ % 2 == 1 } @list )
      {
         print "An odd number exists in the given list.\n";
      }

You can also verify that all the numbers in a list are even numbers
with the "every" function like:

      my @list = (2, 4, 5, 6, 8);

      if ( every { $_ % 2 == 0 } @list )
      {
         print "All number in given list are even.\n";
      }

   These functions don't suffer then same inefficiency discussed in
the perldoc because they will stop examining the list once the "some"
function verifies the answer is true, or the "every" function verifies
that the answer is false.

   A few things to note:

   * The "some" function always returns false on an empty list.
     (because no element exists that passes the test).
   * The "every" function always returns true on an empty list
     (because no element exists that fails the test).
   * These functions always return a true or false (boolean) value.
     They DO NOT return a list, like "map" and "grep" do.
   * These functions must be specified with a block of code.
     In other words, where "grep" can be used like this:
         grep m/^a/i, @list
     or like this:
         grep { m/^a/i } @list
     the "some" and "every" functions can only be used like this:
         some { m/^a/i } @list
         every { m/^a/i } @list


   That's all I have to say on this subject, so Happy Perling!

   -- Jean-Luc Romano
0
jl_post
11/13/2004 2:54:29 PM
comp.lang.perl.misc 33233 articles. 2 followers. brian (1246) is leader. Post Follow

5 Replies
833 Views

Similar Articles

[PageSpeed] 56

J. Romano wrote:
> Dear Perl community,
> 
>    Recently I've been learning some Lisp on my own, and I've come
> across two Lisp functions that I wish Perl had:  "some" and "every".
> 
>    They work somewhat like "grep" and "map" in that they operate on a
> list given a function, but they return only a boolean value.
 >
>    One work-around is mentioned in 'perldoc -q "element is
> contained"', so I won't discuss it here.  Another work-around is to
> create a module that defines the "some" and "every" functions to be
> used similarly to how Perl's "grep" and "map" functions are called. 
> I've written the code, and here it is (more discussion follows):
> 

The other work-around would be to use List::Any.
http://search.cpan.org/~vparseval/List-Any-0.03/lib/List/Any.pm

Rhesa
0
Rhesa
11/13/2004 3:05:15 PM
Rhesa Rozendaal wrote:
> J. Romano wrote:
> 
>> Dear Perl community,
>>
>>    Recently I've been learning some Lisp on my own, and I've come
>> across two Lisp functions that I wish Perl had:  "some" and "every".
>>
>>    They work somewhat like "grep" and "map" in that they operate on a
>> list given a function, but they return only a boolean value.
> 
>  >
> 
>>    One work-around is mentioned in 'perldoc -q "element is
>> contained"', so I won't discuss it here.  Another work-around is to
>> create a module that defines the "some" and "every" functions to be
>> used similarly to how Perl's "grep" and "map" functions are called. 
>> I've written the code, and here it is (more discussion follows):
>>
> 
> The other work-around would be to use List::Any.
> http://search.cpan.org/~vparseval/List-Any-0.03/lib/List/Any.pm
> 
> Rhesa

It seems Tassilo merged the code of List::Any into List::MoreUtils.
0
Rhesa
11/13/2004 3:15:47 PM
Also sprach Rhesa Rozendaal:

> Rhesa Rozendaal wrote:

>> The other work-around would be to use List::Any.
>> http://search.cpan.org/~vparseval/List-Any-0.03/lib/List/Any.pm
>> 
>> Rhesa
>
> It seems Tassilo merged the code of List::Any into List::MoreUtils.

Not so much merged into but renamed to. I was made aware that
List::MoreUtils would be a better namespace choice. So that means the
latest release is always at 

    http://search.cpan.org/~vparseval/List-MoreUtils/

Tassilo
-- 
$_=q#",}])!JAPH!qq(tsuJ[{@"tnirp}3..0}_$;//::niam/s~=)]3[))_$-3(rellac(=_$({
pam{rekcahbus})(rekcah{lrePbus})(lreP{rehtonabus})!JAPH!qq(rehtona{tsuJbus#;
$_=reverse,s+(?<=sub).+q#q!'"qq.\t$&."'!#+sexisexiixesixeseg;y~\n~~dddd;eval
0
Tassilo
11/13/2004 3:25:44 PM

Tassilo v. Parseval wrote:

> Also sprach Rhesa Rozendaal:
> 
> 
>>Rhesa Rozendaal wrote:
> 
> 
>>>The other work-around would be to use List::Any.
>>>http://search.cpan.org/~vparseval/List-Any-0.03/lib/List/Any.pm
>>>
>>>Rhesa
>>
>>It seems Tassilo merged the code of List::Any into List::MoreUtils.
> 
> 
> Not so much merged into but renamed to. I was made aware that
> List::MoreUtils would be a better namespace choice. So that means the
> latest release is always at 
> 
>     http://search.cpan.org/~vparseval/List-MoreUtils/
> 
> Tassilo

Any chance the old apply() function could find a home there?


=item apply BLOCK LIST

Similar to C<map> in that it evaluates BLOCK setting C<$_> to each
element of LIST in turn.  C<apply> returns a list made up of the
values of C<$_> after BLOCK has been evaluated.  In a scalar context
the last element is returned.  Unlike C<map> and C<grep> the elements
of LIST are not altered.

     @foo = apply { s/\s+/ /g } @bar; # canonicalise whitespace

This function is nothing more than syntactic sugar for people who find
the following syntax unpalatable.

     for (@foo = @bar) { s/\s+/ /g }

=cut

sub apply (&@) {
     my $action = shift;
     &$action for my @values = @_;
     wantarray ? @values : $values[-1];
}

0
Brian
11/13/2004 3:44:04 PM
Also sprach Brian McCauley:

> Tassilo v. Parseval wrote:

>> Not so much merged into but renamed to. I was made aware that
>> List::MoreUtils would be a better namespace choice. So that means the
>> latest release is always at 
>> 
>>     http://search.cpan.org/~vparseval/List-MoreUtils/
>> 
>> Tassilo
>
> Any chance the old apply() function could find a home there?

Ah, sure. I'm always looking for more useful list operations.

> sub apply (&@) {
>      my $action = shift;
>      &$action for my @values = @_;
>      wantarray ? @values : $values[-1];
> }

I can use that verbatimly for the pure-Perl implementation. The XS one
shouldn't be too tricky either. I'll do that tomorrow.

Tassilo
-- 
$_=q#",}])!JAPH!qq(tsuJ[{@"tnirp}3..0}_$;//::niam/s~=)]3[))_$-3(rellac(=_$({
pam{rekcahbus})(rekcah{lrePbus})(lreP{rehtonabus})!JAPH!qq(rehtona{tsuJbus#;
$_=reverse,s+(?<=sub).+q#q!'"qq.\t$&."'!#+sexisexiixesixeseg;y~\n~~dddd;eval
0
Tassilo
11/13/2004 7:13:48 PM
Reply: