http://www.perlmonks.org?node_id=1214135


in reply to Re^2: Can I check if a loop's scope is entered for the first time? (Devel::Callsite)
in thread Can I check if a loop's scope is entered for the first time?

Apologies, Rolf. I misunderstood the actual problem.
And, yes, it is an issue with Var::Pairs too.
And, yes, I will now look at solving it. ;-)

Damian
  • Comment on Re^3: Can I check if a loop's scope is entered for the first time? (Devel::Callsite)

Replies are listed 'Best First'.
Re^4: Can I check if a loop's scope is entered for the first time? (Devel::Callsite)
by TheDamian (Vicar) on May 07, 2018 at 05:03 UTC

    Well, that took longer than I expected. ;-)

    I just uploaded a new release of Var::Pairs (version 0.003005)
    that solves the problem of resetting an iterator on premature termination
    of the surrounding loop. It does so by using Scope::Upper::reap()
    to install a "destructor" for each iterator when execution leaves
    the surrounding scope.

    Take another look; I think it's likely this technique would also work for you.

    Damian

      I think it's likely this technique would also work for you.

      In fact, more than just likely...

      sub from (&$) { use Devel::Callsite qw< callsite context >; use Scope::Upper qw< reap UP >; my ($iterator_source) = @_; # Uniquely identify this call, according to its lexical context... my $ID = callsite() . context(); # Install the iterator... state %iterator_for; $iterator_for{$ID} //= $iterator_source->(); # Install destructor for iterator at end of caller's block... reap { delete $iterator_for{$ID} } UP UP; # Iterate... return $iterator_for{$ID}->($_[1]); } for my $limit (reverse 1..5) { while ( from{countdown($limit)} my $a ) { print "$a: "; } print "\n"; } sub countdown{ my $val = shift; my $iter = sub { if ($val--) { $_[0]=$val; return 1; } return; # stop iteration }; return $iter; }

        What the heck...I just uploaded another new release of Var::Pairs
        (version 0.004000), which lets you write this:

        use Var::Pairs; sub countdown { my $n = shift; return sub { return if $n < 0; # Indicates iterator exhausted return $n--; # Next value iterated } } for my $limit (reverse 1..5) { while (my ($a) = each_value countdown($limit)) { print $a, ':'; } say q{}; }

        ...which is very close to what you wanted, I believe.

        (And, yes, the each_value() subroutine is doing a lot of very hard paddling
        under the surface to correctly ignore all those repeated calls to countdown($limit)
        in the while loop. ;-)

        And, obviously, it would be far more efficient to write the iteration:
        for my $limit (reverse 1..5) { my $iter = countdown($limit); while (my ($a) = each_value $iter) { print $a, ':'; } say q{}; }

        Damian