Reputation: 827
New to Perl, I'm trying to define a subset of characters and then output all possible combinations (max 8 characters) of them in a text file.
I'm really not comfortable with the 'default variable' or other aspects of Perl, so I'm starting with pseudo code in hopes that someone can help me with the specifics (I learn best from examples).
#Define output file
$filename=output.dat
$standard_output->$filename
#Define list
$list[]=regex/a..z 0..9/
#Cycle through iterations
foreach $letter1 in $list{
print $list[$letter]
}
foreach $letter1 in $list{
foreach $letter2 in $list{
print $list[$letter1] $list[$letter2]
}
}
...
foreach letter1 in list{
foreach letter2 in list{
foreach letter3 in list{
foreach letter4 in list{
foreach letter5 in list{
foreach letter6 in list{
foreach letter7 in list{
foreach letter8 in list{
print list[letter1] list[letter2] list[letter3] list[letter4] list[letter5] list[letter6] list[letter7] list[letter8]
}
}
}
}
}
}
}
}
As you can clearly see, I'm very new at this. Can someone help me get my bearings with Perl?
Upvotes: 0
Views: 2076
Reputation: 132863
My Set::CrossProduct module makes this easy, and it does it without stack- or memory-busting recursion:
use v5.10;
use Set::CrossProduct;
my $min = 2;
my $max = 4;
my $set = [ qw( a b 1 2 ) ];
foreach my $length ( $min .. $max ) {
say "Getting combinations of length $length";
my $cross = Set::CrossProduct->new(
[ ( $set ) x $length ]
);
while( my $tuple = $cross->get ) {
say join '', @$tuple;
}
}
Upvotes: 4
Reputation: 57640
Functional Programming* to the rescue! The following code won't be too efficient, but far cleaner.
We will define a function that takes a number and a list as arguments. The number denotes how many times we have to recurse, the list holds the letters from the outer levels.
#!/usr/bin/perl
use strict; use warnings;
my @list = 'a' .. 'z'; # lowercase letters
sub combinations {
my ($recursions, @letters) = @_; # unpack the arguments
if ($recursions == 0) {
print @letters, "\n"; # print the letters, append newline
} else {
# do a loop
$recursions--; # reduce level
for my $letter (@list) {
combinations($recursions, @letters, $letter);
}
}
}
We can call that sub with combinations(8);
to get the expected result.
The ..
is the range operator and produces a list. It works with alphabetic characters too. You will want 'a' .. 'z', 0 .. 9
.
It works, although you might want to use a smaller @list
for testing.
This produces all fixed-length strings. To get all strings up to a given length, just do
combinations($length) foreach my $length (1 .. 8);
(breadth-first), or include
print @list, "\n";
just before the for
-loop in the else
-branch for depth-first.
Footnotes:
(*) No, not really, but this is something close. Functional Programming does not require any loops at all.
Upvotes: 3