I had a look at the very first piece of code on the page you referred to:
sub c{my$n=-1+shift;$n?map{my$c=$_;map$c.$_,c($n,@_)}@_:@_}
I have spread it out a bit to make it more readable; also I have made some changes to it to make it clearer (see combinations
):
#!/usr/bin/perl
use strict;
use warnings;
sub c {
my $n=-1+shift;
$n ? map{
my $c = $_;
map $c . $_ , c($n ,@_)
} @_
: @_;
}
sub combinations {
my $number = shift; # remove the first item from @_
my @chars = @_; # the remainder of @_
$number --; # decrement $number, so that you will eventually exit
# from this recursive subroutine (once $number == 0)
if ($number) { # true as long as $number != 0 and $number not undef
my @result;
foreach my $char (@chars) {
my @intermediate_list = map { $char . $_ } combinations($number, @chars);
push @result, @intermediate_list;
}
return @result; # the current concatenation result will be used for creation of
# @intermediate_list in the 'subroutine instance' that called 'combinations'
}
else {
return @chars;
}
}
print join " ", combinations(2, "A", "B");
print "\n";
print join " ", c(2, "A", "B");
print "\n\n";
print join " ", combinations(3, "A", "B");
print "\n";
print join " ", c(3, "A", "B");
print "\n";
Both versions work in the same way, and they produce exactly the same output:
AA AB BA BB
AA AB BA BB
AAA AAB ABA ABB BAA BAB BBA BBB
AAA AAB ABA ABB BAA BAB BBA BBB
I included some comments in the code, but perhaps a lengthier explanation is in order!? Well, here's an example to illustrate how things work: let's say we've got two items, "A" and "B", and we want to get all possible combinations of 2 of these items. In that case, $number
will initially be equal to 2 (as we want to get pairs), and @chars
will be equal to ('A', 'B')
.
The first time combinations
is called, $number
is decremented to 1, thus the if
condition is met, and we enter the foreach
loop. This first sets $char
to 'A'. It then calls combinations(1, ('A', 'B'))
. As $number
always gets decremented when the subroutine is called, $number
is 0 in this 'child subroutine', consequently the child simply returns ('A', 'B'). Thus:
@intermediate_list = map { $char . $_ } ('A', 'B'); # $char eq 'A'
map
then takes both 'A' and 'B' and concatenates each with 'A' ($char), thus @intermediate_list
is ('AA', 'AB'). In the next round of the foreach
loop, the same is done with $char = B
, which sets @intermediate_list
to ('BA', 'BB').
In each round the contents of @intermediate_list
are pushed into the result list, hence @result
eventually contains all possible combinations.
If you want to get triplets instead of pairs, you will obviously start with $number = 3
, and combinations
will be called three times. The second time it's called it will return @result
, i.e. a pair-containing list. Each item from that list will be concatenated with each character of the initial character set.
Okay, I hope this makes sense. Please ask in case something has not become clear.
EDIT: Please see ysth's comment below.