I would like to understand what is happening in each step of the execution of the perl script below, I mean, I know what variables, hash, integer array are, but I don't know how they are interacting in this powerset construct using lazy evaluation.
I would also like to know which factors determine which step is the progress of the sub powerset(&@)
subroutine.
For example, I would like to start printing from the sixth subset, not the first, so which values of which variables should I substitute?
use strict;
use warnings;
sub powerset(&@) {
my $callback = shift;
my $bitmask = '';
my $bytes = @_/8;
{
my @indices = grep vec($bitmask, $_, 1), 0..$#_;
$callback->( @_[@indices] );
++vec($bitmask, $_, 8) and last for 0 .. $bytes;
redo if @indices != @_;
}
}
powerset { print "[@_]\n" } 1..21;
my $bytes = @_/8;
: Here @_
is the array input argument so @_ = 1..21
and when evaluated in scalar context it returns the length of the array. So $bytes = 21/8 = 2.625
my @indices = grep vec($bitmask, $_, 1), 0..$#_;
Here $#_
is the last index in @_
which is 20. So this runs grep on the array 0..20
. For each element in the array, it is checked if the corresponding bit value in $bitmask
is set, if it is set it is saved in the @indices
array.$callback->( @_[@indices] );
: This calls the callback function with the array of indices which corresponds to the bits set in $bitmask
. Since $bitmask
is initially empty, in the first iteration, @indices
will be equal to the empty array []
.++vec($bitmask, $_, 8) and last for 0 .. $bytes;
: Loops from 0..2 since $bytes == 2.625
it is rounded down to the nearest integer value. For each bytes index value in 0..2
the corresponding byte in $bitmask
(treated now as an array of bytes) is incremented. The new byte value is returned from vec
, if the returned value is nonzero the for loop exits (due to the and last
part. However, if the value of the byte was 255, ++vec($bitmask, $_, 8)
will return a 0 (the byte value wraps around to zero) and the next iteration of the for 0..$bytes
for
loop will execute.redo if @indices != @_;
runs the block (lines 7-12) again if the length of the @indices
array is different from the length of @_
(i.e.: 21).