in reply to Re^2: Designing a DWIMish interface for generator function
in thread Designing a DWIMish interface for generator function

You need something external to the loop to keep the state.

You could use a combination of the address of the calling opcode as a key to get the appropriate state, but you'd have no way to reset to the counter if you did that, so you wouldn't be able to last/return/die from within the loop.

Just look at each for example. To properly use each, you need to do:

keys %hash; # Reset iterator while (my ($k, $v) = each(%hash)) { ... }

And even then, it doesn't nest because the iterator is per-hash, not per-each.

Replies are listed 'Best First'.
Re^4: Designing a DWIMish interface for generator function
by LanX (Saint) on Feb 01, 2010 at 04:42 UTC
    Did you have a look at the code I linked to? It keeps the state, by using the ref of the iterator-variable as key:

    use strict; use warnings; { my %lists; sub deal { my $aref=shift; my $id= \$_[0]; #print $aref,$id; $lists{$id}=$aref if (! exists $lists{$id} ); if (@{$lists{$id}}){ for(@_) { $_=shift @{$lists{$id}}; } return 1; } else { delete $lists{$id}; return 0; } } } $\="\n"; #--------- here we go while (deal [1..9] => my ($a,$b,$c) ){ print $a,$b,$c; while (deal [a=>1,b=>2] => my ($a,$b) ){ print $a,$b; } } #--------- print "EXIT"; __DATA__ 123 a1 b2 456 a1 b2 789 a1 b2

    But I have to admit that till now I haven't thought about last, that's indeed a problem...

    Cheers Rolf

      • You assumed that the iterator variable will be created at the same memory address every pass of the loop. It's a bad assumption. For example, the following code is an infinite loop:

        my @x; while (deal [1..9] => my ($a,$b,$c) ){ print $a,$b,$c; push @x, \$a; }

        Well, maybe not infinite, since deal is leaking memory every pass of the loop...

      • You assumed that the loop will receive a new iterator each time. It's a bad assumption. For example, deal starts at the wrong number the second and third pass of the outer loop in the following code:

        for (1..3) { while (deal [1..9] => my ($a,$b,$c) ){ print $a,$b,$c; last; } }

      Just like you need to call keys before each, you'd need to reset the iterator before entering the loop. You'd need

      init_deal my ($a,$b,$c) while (deal [1..9] => ($a,$b,$c) ){ ... }
        Hi

        As I already admitted last is really an unexpected problem... 8(

        (Hmm ... maybe there's a way by trying to catch the destruction of the iterator variable... )

        Talking about positional subs and leaking I already found a nice workaround by using code-blocks!¹ 8)

        sub noleak (&$) { my $cr=shift; $_[0]=$cr } my @a; for (0..9) { noleak {0,5} my $a; push @a,$a; print $a }

        The code-reference seems - as expected - to be precalculated at compile time and is fix!

        /usr/bin/perl -w /home/lanx/B/PL/PM/iter_leak.pl CODE(0xa099b48)CODE(0xa099b48)CODE(0xa099b48)CODE(0xa099b48)CODE(0xa09 +9b48)CODE(0xa099b48)CODE(0xa099b48)CODE(0xa099b48)CODE(0xa099b48)CODE +(0xa099b48)

        Or do you have an idea to break this approach?

        Cheers Rolf

        UPDATE:

        ARGHH it only works when using fixed values...

        sub noleak (&$) { my $cr=shift; $_[0]=$cr } my ($start,$end)=(1,20); my @a; for (0..9) { $start++; noleak {$start,$end} my $a; push @a,$a; print $a }

        output

        CODE(0x8e29760)CODE(0x8e47ca8)CODE(0x8e47d08)CODE(0x8e47d68)CODE(0x8e4 +7dc8)CODE(0x8e47e28)CODE(0x8e47e88)CODE(0x8e47ee8)CODE(0x8e47f48)CODE +(0x8e47fa8)

        ... but commenting out the push produces:

        CODE(0x81f7760)CODE(0x81f7760)CODE(0x81f7760)CODE(0x81f7760)CODE(0x81f +7760)CODE(0x81f7760)CODE(0x81f7760)CODE(0x81f7760)CODE(0x81f7760)CODE +(0x81f7760)

        and there is no need to propagate the coderef outside the sub...

        UPDATE

        ¹) I ran some tests, better forget it...it really only seems to work reliably with constant values in the code-block ...seems to be an optimization issue.

        UPDATE I'm going to run some tests with state.