sub hanoi {
my ($disks, $start, $end, $via) = @_;
return () if $disks == 0;
return ( hanoi($disks-1, $start, $via, $end),
[$start, $end],
hanoi($disks-1, $via, $end, $start)
);
}
####
sub iter_hanoi {
my ($disks, $start, $end, $via) = @_;
return sub{} if $disks == 0; # Base case is empty, replace with empty sub
# List of iterators, wrapped in sub{}s to delay their evaluation:
my @sub_iter = (
sub { iter_hanoi($disks-1, $start, $via, $end) },
# Explicit return is assigned to array for iterator to shift
sub {
my @middle_val = ([$start, $end]);
sub {shift @middle_val}
},
sub { iter_hanoi($disks-1, $via, $end, $start) },
);
# Below here is boilerplate: if you've done the above steps right, just plug
# this in, and it works. It returns the first iterator from the list that
# returns anything.
# Grab and unwrap an iterator from the list
my $iter = (shift @sub_iter)->();
return sub {
my $rval;
$iter = (shift @sub_iter)->()
until ($rval = $iter->() or @sub_iter == 0);
return $rval;
}
}
##
##
sub iter_choose_n {
my $n = pop;
# Base cases get assigned to an array, which the iterator shifts through
my @base_case = ([]);
return sub{ shift @base_case } if $n == 0 or $n > @_;
@base_case = (\@_);
return sub { shift @base_case } if $n == @_;
# otherwise..
my ($first, @rest) = @_;
my @sub_iter = (
sub {
my $recurse = iter_choose_n(@rest, $n-1);
my $set;
sub { ($set = $recurse->()) ? [$first, @$set] : () }
},
sub { iter_choose_n(@rest, $n) }
);
# Below here is boilerplate: if you've done the above steps right, just plug
# this in, and it works. It returns the first iterator from the list that
# returns anything.
# Grab and unwrap an iterator from the list
my $iter = (shift @sub_iter)->();
return sub {
my $rval;
$iter = (shift @sub_iter)->()
until ($rval = $iter->() or @sub_iter == 0);
return $rval;
}
}