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;
CodePudding user response:
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 array0..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 in0..2
the corresponding byte in$bitmask
(treated now as an array of bytes) is incremented. The new byte value is returned fromvec
, if the returned value is nonzero the for loop exits (due to theand 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 thefor 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).