in reply to Re^5: secret code generator

I wasn't particularly impressed with the readability of any of the previously presented solutions, so I have added a few more solutions below (two of my own: #2 and #3, and one based on NestedLoops: #4). For readability, I like #4 (NestedLoops) best and #3 next best. #2 is an optimized version of #3 which is still much more readable IMO than the other solutions. I have tried to keep the solutions close in spirit to the original #1 (by Tilly). The technique of passing an accumulator is used by a lot of Lisp idioms.

As far as benchmarking goes, #1 is faster than #3, but #2 is the fastest by a substantial margin. #4 (NestedLoops) is dead last. I have included the benchmarking code for you to try yourself.

All the solutions except #1 (the original solution) work by permuting the rightmost "digit" fastest. #1 permutes the leftmost "digit" fastest. Inserting the two calls to reverse (which I have commented out below) slows it down a bit but not enough to affect it's ranking.

```#! /usr/bin/perl -w
use strict;
use Benchmark;
use Algorithm::Loops qw(NestedLoops);

use vars qw(@chars \$loop_cnt \$printit);
\$loop_cnt = 3;
@chars = map chr, 0x21 .. 0x7e;
# @chars = ("a" .. "c");

sub printit {
print @_ if \$printit;
}

#------------------------------------------------

sub doit4 {
my \$x = 1;
while (\$x <= \$loop_cnt) {
NestedLoops(
[ map \@chars, 1 .. \$x++ ],
sub { printit join "", @_, "\n" }
);
}
}

#------------------------------------------------

sub ret_iter3 {
my \$accum = shift;
my \$range = shift;
if (\$range) { ret_iter3( [@\$accum, \$_], @_ ) for @\$range }
else        { printit join "", @\$accum, "\n" }
}

sub nested_for3 {
ret_iter3 [], @_;
}

sub doit3 {
my \$x = 1;
while (\$x <= \$loop_cnt) {
nested_for3 map \@chars, 1 .. \$x++;
}
}

#------------------------------------------------

sub ret_iter2 {
my \$accum = shift;
my \$range = shift;
if (@_) {
for (@\$range) {
push @\$accum, \$_;
ret_iter2(\$accum, @_);
pop @\$accum;
}
} else {
printit join "", @\$accum, \$_, "\n" for @\$range;
}
}

sub nested_for2 {
ret_iter2 [], @_;
}

sub doit2 {
my \$x = 1;
while (\$x <= \$loop_cnt) {
nested_for2 map \@chars, 1 .. \$x++;
}
}

#------------------------------------------------

sub doit1 {
my \$x = 0;
while (++\$x <= \$loop_cnt) {
nested_for1(
sub {printit join "", @_, "\n";}
, map \@chars, 1..\$x
);
#     nested_for1(
#        sub {printit join "", reverse(@_), "\n";}
#      , reverse(map \@chars, 1..\$x)
#     );
}
}

sub nested_for1 {
ret_iter(@_)->();
}

sub ret_iter {
my \$fn = shift;
my \$range = shift;
my \$sub = sub {\$fn->(\$_, @_) for @\$range};
return @_ ? ret_iter(\$sub, @_) : \$sub;
}

#------------------------------------------------

# \$printit = 1;
# doit1;
# doit2;
# doit3;
# doit4;
# exit;

timethese (-10, {
doit1 => 'doit1',
doit2 => 'doit2',
doit3 => 'doit3',
doit4 => 'doit4',
});