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