Started this before LanX' answer (trying to implement whatever I understood reading the HOP):
use strict;
use warnings;
use 5.010; # defined-or
use Data::Dump 'dd';
sub get_iterator {
my ( $x_to, $y_to, $x_from, $y_from ) = @_;
$x_from //= 0;
$y_from //= 0;
# store partial solutions to explore, to follow left or right
# branches, respectively
my @left_agenda = [[ $x_from, $y_from ]];
my @right_agenda = [[ $x_from, $y_from ]];
return sub {
LOOP: {
return undef unless @left_agenda || @right_agenda;
my ( $ref, $right );
unless ( $ref = pop @left_agenda ) {
$ref = pop @right_agenda;
$right = 1
}
my @path = @$ref;
my ( $x, $y ) = @{ $path[ -1 ] };
$x ++;
$y ++ if $right;
if ( $x <= $x_to and $y <= $y_to ) {
push @path, [ $x, $y ];
return \@path if $x == $x_to and $y == $y_to;
push @left_agenda, \@path;
push @right_agenda, \@path;
}
redo LOOP;
}
}
}
my $iter = get_iterator( 5, 2 );
my $solution;
dd $solution while $solution = $iter-> ();
Update. Slightly refactored version, eliminating "left agenda", i.e. pushing and immediately popping. Still, it's ugly because from initial point we can descend left and right, but from any partial solution from agenda - right only, left direction already exhausted. Hence, the "init" flag.
use strict;
use warnings;
use 5.010; # defined-or
use Data::Dump 'dd';
sub get_iterator {
my ( $x_to, $y_to, $x_from, $y_from ) = @_;
$x_from //= 0;
$y_from //= 0;
my @agenda = [[ $x_from, $y_from ]];
my $init = 1;
return sub {
my @path;
( @path, $init ) = @{ $agenda[ -1 ]} if $init;
LOOP: {
return undef unless @path || @agenda;
my $right;
unless ( @path ) {
@path = @{ pop @agenda };
$right = 1
}
my ( $x, $y ) = @{ $path[ -1 ] };
$x ++;
$y ++ if $right;
if ( $x <= $x_to and $y <= $y_to ) {
push @path, [ $x, $y ];
return \@path if $x == $x_to and $y == $y_to;
push @agenda, [ @path ]
}
else {
@path = ();
}
redo LOOP;
}
}
}
my $iter = get_iterator( 5, 2 );
my $solution;
dd $solution while $solution = $iter-> ();