The states are
sub bits {
my ($n, $i) = @_;
return map { my $bit = $i & 1; $i >>= 1; $bit } 0..$n-1;
}
my $n = 3;
for my $i (0 .. (1<<$n)-1) {
my @bits = bits($n, $i);
print("@bits\n");
}
If you don't have &
, <<
and >>
(which would be really really weird as all CPUs have these), or if you want more than two states, you can use the following:
sub bits {
my ($b, $n, $i) = @_;
return map { my $bit = $i % $b; $i = ($i-$bit)/$b; $bit } 0..$n-1;
}
my $n = 3;
my $b = 2;
for my $i (0 .. ($b**$n)-1) {
my @bits = bits($b, $n, $i);
print("@bits\n");
}
Optimized:
sub get_next {
my ($b, $bits) = @_;
for (@$bits) {
return 1 if ++$_ != $b;
$_ = 0;
}
return 0;
}
my $n = 3;
my $b = 2;
my @bits = (0) x $n;
while (1) {
print("@bits\n");
last if !get_next($b, \@bits);
}
Benchmark results for n=3,
Rate tommaso_pasini ikegami ikegami_inline
tommaso_pasini 34462/s -- -23% -49%
ikegami 44612/s 29% -- -34%
ikegami_inline 68061/s 97% 53% --
Benchmark results for n=10,
Rate tommaso_pasini ikegami ikegami_inline
tommaso_pasini 271/s -- -33% -58%
ikegami 403/s 48% -- -38%
ikegami_inline 644/s 138% 60% --
Benchmarking code:
use strict;
use warnings;
use Benchmark qw( cmpthese );
sub _tommaso_pasini {
my ($n, $bits, $i) = @_;
if ($i == $n) {
# ...
return;
}
$bits->[$i] = 0;
_tommaso_pasini($n, $bits, $i+1);
$bits->[$i] = 1;
_tommaso_pasini($n, $bits, $i+1);
}
sub tommaso_pasini {
my ($n) = @_;
_tommaso_pasini($n, [], 0);
}
sub ikegami_next {
my ($bits) = @_;
for (@$bits) {
return 1 if ++$_ != 2;
$_ = 0;
}
return 0;
}
sub ikegami {
my ($n) = @_;
my $bits = [ (0) x $n ];
while (1) {
# ...
last if !ikegami_next($bits);
}
}
sub ikegami_inline {
my ($n) = @_;
my $bits = [ (0) x $n ];
OUTER: while (1) {
# ...
for (@$bits) {
next OUTER if ++$_ != 2;
$_ = 0;
}
last;
}
}
for my $n (3, 10) {
cmpthese(-3, {
ikegami => sub { ikegami($n) },
ikegami_inline => sub { ikegami_inline($n) },
tommaso_pasini => sub { tommaso_pasini($n) },
});
}