=head1 NAME
-perlfaq4 - Data Manipulation ($Revision: 9491 $)
+perlfaq4 - Data Manipulation ($Revision: 10394 $)
=head1 DESCRIPTION
(contributed by brian d foy)
If you can avoid it, don't, or if you can use a templating system,
-such as C<Text::Template> or C<Template> Toolkit, do that instead.
+such as C<Text::Template> or C<Template> Toolkit, do that instead. You
+might even be able to get the job done with C<sprintf> or C<printf>:
+
+ my $string = sprintf 'Say hello to %s and %s', $foo, $bar;
However, for the one-off simple case where I don't want to pull out a
full templating system, I'll use a string that has two Perl scalar
variables in it. In this example, I want to expand C<$foo> and C<$bar>
-to their variable's values.
+to their variable's values:
my $foo = 'Fred';
my $bar = 'Barney';
C</e> flag. The first C</e> evaluates C<$1> on the replacement side and
turns it into C<$foo>. The second /e starts with C<$foo> and replaces
it with its value. C<$foo>, then, turns into 'Fred', and that's finally
-what's left in the string.
+what's left in the string:
$string =~ s/(\$\w+)/$1/eeg; # 'Say hello to Fred and Barney'
The C</e> will also silently ignore violations of strict, replacing
-undefined variable names with the empty string.
-
-I could also pull the values from a hash instead of evaluating
-variable names. Using a single C</e>, I can check the hash to ensure
-the value exists, and if it doesn't, I can replace the missing value
-with a marker, in this case C<???> to signal that I missed something:
+undefined variable names with the empty string. Since I'm using the
+C</e> flag (twice even!), I have all of the same security problems I
+have with C<eval> in its string form. If there's something odd in
+C<$foo>, perhaps something like C<@{[ system "rm -rf /" ]}>, then
+I could get myself in trouble.
+
+To get around the security problem, I could also pull the values from
+a hash instead of evaluating variable names. Using a single C</e>, I
+can check the hash to ensure the value exists, and if it doesn't, I
+can replace the missing value with a marker, in this case C<???> to
+signal that I missed something:
my $string = 'This has $foo and $bar';
If you are testing only once, the standard module C<List::Util> exports
the function C<first> for this purpose. It works by stopping once it
-finds the element. It's written in C for speed, and its Perl equivalant
+finds the element. It's written in C for speed, and its Perl equivalent
looks like this subroutine:
sub first (&@) {
my $element = $array[ rand @array ];
=head2 How do I permute N elements of a list?
+X<List::Permuter> X<permute> X<Algorithm::Loops> X<Knuth>
+X<The Art of Computer Programming> X<Fischer-Krause>
-Use the C<List::Permutor> module on CPAN. If the list is actually an
+Use the C<List::Permutor> module on CPAN. If the list is actually an
array, try the C<Algorithm::Permute> module (also on CPAN). It's
-written in XS code and is very efficient.
+written in XS code and is very efficient:
use Algorithm::Permute;
+
my @array = 'a'..'d';
my $p_iterator = Algorithm::Permute->new ( \@array );
+
while (my @perm = $p_iterator->next) {
print "next permutation: (@perm)\n";
}
For even faster execution, you could do:
use Algorithm::Permute;
+
my @array = 'a'..'d';
+
Algorithm::Permute::permute {
print "next permutation: (@array)\n";
} @array;
-Here's a little program that generates all permutations of
-all the words on each line of input. The algorithm embodied
-in the C<permute()> function is discussed in Volume 4 (still
-unpublished) of Knuth's I<The Art of Computer Programming>
-and will work on any list:
+Here's a little program that generates all permutations of all the
+words on each line of input. The algorithm embodied in the
+C<permute()> function is discussed in Volume 4 (still unpublished) of
+Knuth's I<The Art of Computer Programming> and will work on any list:
#!/usr/bin/perl -n
# Fischer-Krause ordered permutation generator
}
}
- permute {print"@_\n"} split;
+ permute { print "@_\n" } split;
+
+The C<Algorithm::Loops> module also provides the C<NextPermute> and
+C<NextPermuteNum> functions which efficiently find all unique permutations
+of an array, even if it contains duplicate values, modifying it in-place:
+if its elements are in reverse-sorted order then the array is reversed,
+making it sorted, and it returns false; otherwise the next
+permutation is returned.
+
+C<NextPermute> uses string order and C<NextPermuteNum> numeric order, so
+you can enumerate all the permutations of C<0..9> like this:
+
+ use Algorithm::Loops qw(NextPermuteNum);
+
+ my @list= 0..9;
+ do { print "@list\n" } while NextPermuteNum @list;
=head2 How do I sort an array by (anything)?
$hash{'d'} is false
defined $hash{'d'} is true
defined $hash{'a'} is true
- exists $hash{'a'} is true (Perl5 only)
+ exists $hash{'a'} is true (Perl 5 only)
grep ($_ eq 'a', keys %hash) is true
If you now say
$hash{'d'} is false
defined $hash{'d'} is true
defined $hash{'a'} is FALSE
- exists $hash{'a'} is true (Perl5 only)
+ exists $hash{'a'} is true (Perl 5 only)
grep ($_ eq 'a', keys %hash) is true
Notice the last two: you have an undef value, but a defined key!
$hash{'d'} is false
defined $hash{'d'} is true
defined $hash{'a'} is false
- exists $hash{'a'} is FALSE (Perl5 only)
+ exists $hash{'a'} is FALSE (Perl 5 only)
grep ($_ eq 'a', keys %hash) is FALSE
See, the whole entry is gone!
=head2 How do I reset an each() operation part-way through?
-Using C<keys %hash> in scalar context returns the number of keys in
-the hash I<and> resets the iterator associated with the hash. You may
-need to do this if you use C<last> to exit a loop early so that when
-you re-enter it, the hash iterator has been reset.
+(contributed by brian d foy)
+
+You can use the C<keys> or C<values> functions to reset C<each>. To
+simply reset the iterator used by C<each> without doing anything else,
+use one of them in void context:
+
+ keys %hash; # resets iterator, nothing else.
+ values %hash; # resets iterator, nothing else.
+
+See the documentation for C<each> in L<perlfunc>.
=head2 How can I get the unique keys from two hashes?
=head1 REVISION
-Revision: $Revision: 9491 $
+Revision: $Revision: 10394 $
-Date: $Date: 2007-05-02 13:14:13 +0200 (Wed, 02 May 2007) $
+Date: $Date: 2007-12-09 18:47:15 +0100 (Sun, 09 Dec 2007) $
See L<perlfaq> for source control details and availability.