# Given a list of M items and a number N,
# generate all size-N subsets of M
sub choose_n {
my $n = pop;
# Base cases
return [] if $n == 0 or $n > @_;
return [@_] if $n == @_;
# otherwise..
my ($first, @rest) = @_;
# combine $first with all N-1 combinations of @rest,
# and generate all N-sized combinations of @rest
my @include_combos = choose_n(@rest, $n-1);
my @exclude_combos = choose_n(@rest, $n);
return ( (map {[$first, @$_]} @include_combos)
, @exclude_combos );
}
####
sub iter_choose_n {
my $n = pop;
# Base cases
my $once = 0;
return sub {$once++ ? () : []} if $n == 0 or $n > @_;
my ($first, @rest) = @_;
return sub {$once++ ? () : [$first, @rest]} if $n == @_;
##
##
# otherwise..
my $include_iter = iter_choose_n(@rest, $n-1);
my $exclude_iter = iter_choose_n(@rest, $n);
return sub {
if (my $set = $include_iter->()) {
return [$first, @$set];
}
else {
return $exclude_iter->();
}
}
##
##
# otherwise..
my $include_iter = iter_choose_n(@rest, $n-1);
my $exclude_iter;
return sub {
if (my $set = $include_iter->()) {
return [$first, @$set];
}
else {
$exclude_iter ||= iter_choose_n(@rest, $n);
return $exclude_iter->();
}
}
}
##
##
# otherwise..
my $include_iter = iter_choose_n(@rest, $n-1);
my $exclude_iter;
return sub {
if ($include_iter and my $set = $include_iter->()) {
return [$first, @$set];
}
else {
if ($include_iter) {
undef $include_iter;
$exclude_iter = iter_choose_n(@rest, $n);
}
return $exclude_iter->();
}
}
}