Lisp's "some" and "every" functions

J

J. Romano

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
 
R

Rhesa Rozendaal

J. Romano said:
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
 
B

Brian McCauley

Tassilo said:
Also sprach Rhesa Rozendaal:




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];
}
 
T

Tassilo v. Parseval

Also sprach Brian McCauley:
Tassilo v. Parseval wrote:

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
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,007
Latest member
obedient dusk

Latest Threads

Top