in reply to Re^5: secret code generator
in thread 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', });