Algorithm-Loops
view release on metacpan or search on metacpan
lib/Algorithm/Loops.pm view on Meta::CPAN
or
my $line= Filter { s/\s+$// } scalar <IN>;
[ Note that Filter can be used in a scalar
context but always puts its arguments in a
list context. So we need to use C<scalar> or
something similar if we want to read only one
line at a time from C<IN> above. ]
Want to sort strings that contain mixtures of
letters and natural numbers (non-negative
integers) both alphabetically and numerically
at the same time? This simple way to do a
"natural" sort is also one of the fastest.
Great for sorting version numbers, file names,
etc.:
my @sorted= Filter {
s#\d{2}(\d+)#\1#g
} sort Filter {
s#(\d+)# sprintf "%02d%s", length($1), $1 #g
} @data;
[ Note that at least some versions of Perl have a bug that breaks C<sort>
if you write C<sub {> as part of building the list of items to be sorted
but you don't provide a comparison routine. This bug means we can't
write the previous code as:
my @sorted= Filter {
s#\d{2}(\d+)#\1#g
} sort Filter sub {
s#(\d+)# sprintf "%02d%s", length($1), $1 #g
}, @data;
because it will produce the following error:
Undefined subroutine in sort
in some versions of Perl. Some versions of Perl may even require you
to write it like this:
my @sorted= Filter {
s#\d{2}(\d+)#\1#g
} sort &Filter( sub {
s#(\d+)# sprintf "%02d%s", length($1), $1 #g
}, @data );
Which is how I wrote it in ex/NaturalSort.plx. ]
Need to sort names? Then you'll probably want to ignore letter case and
certain punctuation marks while still preserving both:
my @compare= Filter {tr/A-Z'.,"()/a-z/d} @names;
my @indices= sort {$compare[$a] cmp $compare[$b]} 0..$#names;
@names= @names[@indices];
You can also roll your own simple HTML templating:
print Filter {
s/%(\w*)%/expand($1)/g
} $cgi->...,
...
$cgi->...;
Note that it also also works correctly if you change how you output your
HTML and accidentally switch from list to scalar context:
my $html= '';
...
$html .= Filter {
s/%(\w*)%/expand($1)/g
} $cgi->...,
...
$cgi->...;
=head3 Motivation
A reasonable use of map is:
@copy= map {lc} @list;
which sets @copy to be a copy of @list but with all of the elements
converted to lower case. But it is too easy to think that that could
also be done like this:
@copy= map {tr/A-Z/a-z/} @list; # Wrong
The reason why these aren't the same is similar to why we write:
$str= lc $str;
not
lc $str; # Useless use of 'lc' in void context
and we write:
$str =~ tr/A-Z/a-z/;
not
$new= ( $old =~ tr/A-Z/a-z/ ); # Wrong
That is, many things (such as lc) return a modified copy of what they are
given, but a few things (such as tr///, s///, chop, and chomp) modify
what they are given I<in-place>.
This distinction is so common that we have several ways of switching
between the two forms. For example:
$two= $one + $other;
# vs.
$one += $other;
or
$two= substr($one,0,4);
# vs.
substr($one,4)= '';
I've even heard talk of adding some syntax to Perl to allow you to make
things like C<lc> become reflexive, similar to how += is the reflexive
form of +.
But while many non-reflexive Perl operations have reflexive counterparts,
there are a few reflexive Perl operations that don't really have
non-reflexive counterparts: s///, tr///, chop, chomp.
You can write:
my $line= <STDIN>;
( run in 0.740 second using v1.01-cache-2.11-cpan-5b529ec07f3 )