LanX has asked for the wisdom of the Perl Monks concerning the following question:

Hi

I just realized that it's not possible to have independent each iterators for the same hash.

Is there any "lazy" alternative? I.e. w/o copying all keys in advance into an array?

Arrays are easier since the "keys" can be walked by incrementation like in c-style for-loop.

For illustration

DB<140> sub gen { my $href=shift; return sub { join " => ", each %$href } } DB<141> $iter1 =gen \%h; $iter2 =gen \%h DB<142> @h{a..d}=1..4 => (1, 2, 3, 4) DB<143> print &$iter1,"\t" for 1..3 a => 1 d => 4 c => 3 DB<144> print &$iter2,"\t" for 1..3 b => 2 a => 1 DB<145> print &$iter1,"\t" for 1..3 d => 4 c => 3 b => 2 DB<146> print &$iter2,"\t" for 1..3 a => 1 d => 4

It seems impossible to have independent iterators, and copying the keys in advance isn't really efficient...

I already tried aliasing and referencing the hash, w/o effect it's always the same iterator...

UPDATE

Furthermore, this can cause really ugly bugs ...

Cheers Rolf

( addicted to the Perl Programming Language)

PS:

lanx@nc10-ubuntu:~/perl$ perl -version This is perl, v5.10.0 built for i486-linux-gnu-thread-multi

Replies are listed 'Best First'.
Re: Indepedent lazy iterators for the same hash?
by tobyink (Canon) on Jun 30, 2013 at 18:53 UTC

    That's what Var::Pairs is for!

    #!/usr/bin/env perl use strict; use warnings; use Var::Pairs; my %hash = (foo => 1, bar => 2); for my $pair1 (pairs %hash) { for my $pair2 (pairs %hash) { printf "%s %s\n", $pair1, $pair2; } } __END__ bar => 2 bar => 2 bar => 2 foo => 1 foo => 1 bar => 2 foo => 1 foo => 1

    Sadly it uses Data::Alias which is currently broken on Perl 5.18.

    package Cow { use Moo; has name => (is => 'lazy', default => sub { 'Mooington' }) } say Cow->new->name
      Any chance to get a (any) solution into core soon?

      My hopes were not to rely on dependencies...

      update

      sorry no time to read all, for my $next (pairs @array) is a real iterator?

      This would be very nice mister!!! =)

      Cheers Rolf

      ( addicted to the Perl Programming Language)

        Just an idea: use each as the master iterator somewhere hidden, and have references pointing to the lagging pseudo-iterators. Will try tomorrow to build something.

        UPDATE: Here is my attempt. I am not sure whether it works like each as there is absolutely no magic in it. I am sure it will ignore changes in the hash while you iterating over it.

        use strict; use warnings; sub hashIteratorFactory { my $h = shift; my @both; my $exhausted = 0; return sub { my $i = 0; return sub { my ($k, $v); if( !defined $both[$i] ) { if( !$exhausted and (( $k, $v ) = each %$h) ) { push @both, [ $k, $v ]; } else { $exhausted = 1; $i = 0; return wantarray() ? () : undef(); } } else { ( $k, $v ) = @{$both[$i]}; } $i++; return wantarray() ? ( $k, $v ) : $k; } } } my %h; @h{'a'..'d'} = 1..4; my $factory = hashIteratorFactory( \%h ); my $i1 = $factory->(); my $i2 = $factory->(); print "1:",$i1->(),"\n"; print "1:",$i1->(),"\n"; print "2:",$i2->(),"\n"; print "1:",$i1->(),"\n"; print "1:",$i1->(),"\n"; print "2:",$i2->(),"\n"; print "2:",$i2->(),"\n"; print "1:",$i1->(),"\n"; print "1:",$i1->(),"\n"; print "2:",$i2->(),"\n"; print "2:",$i2->(),"\n"; my $i3 = $factory->(); while( my ($k, $v) = $i3->() ) { print "3:$k$v\n"; } print "2:",$i2->(),"\n"; print "1:",$i1->(),"\n";

        EDIT: added another wantarray ? to the final return line

        ... copying the keys in advance isn't really efficient... My hopes were not to rely on dependencies...

        I don't understand the point about real efficiency, but if you want absolutely no dependencies, you can always roll your own iterator. This one simply stops when it's exhausted, but it's easy to imagine simulating the behavior of the each built-in when it reaches the end of iteration.

        >perl -wMstrict -le "my %hash = qw( one uno two dos three tres four quatro five cinco ); sub my_each (\%) { my $hr = shift; my @k = keys %$hr; ;; return sub { return unless @k; my $k = shift @k; return $k, $hr->{$k}; } } ;; my $e1 = my_each %hash; my $e2 = my_each %hash; ;; print qq{e1: @{[ $e1->() ]}; @{[ $e1->() ]}.}; print qq{e2: @{[ $e2->() ]} \n}; ;; print qq{e1: @{[ $e1->() ]}; @{[ $e1->() ]}.}; print qq{e2: @{[ $e2->() ]} \n}; ;; print qq{e1: @{[ $e1->() ]}; @{[ $e1->() ]}.}; print qq{e2: @{[ $e2->() ]} \n}; ;; print qq{e1: @{[ $e1->() ]}; @{[ $e1->() ]}.}; print qq{e2: @{[ $e2->() ]} \n}; " e1: three tres; five cinco. e2: three tres e1: one uno; two dos. e2: five cinco e1: four quatro; . e2: one uno e1: ; . e2: two dos

        Update: Changed the code example slightly to clarify the termination behavior of the iterator.

        I have added another layer of bookkeeping to make it simpler to use assuming that something like HASH(0x7c2300) is a unique identifier for a hash and does not change during runtime.

        use strict; use warnings; sub hashIteratorFactory { my $h = shift; my @both; my $exhausted = 0; return sub { my $i = 0; return sub { my ($k, $v); if( !defined $both[$i] ) { if( !$exhausted and (( $k, $v ) = each %$h) ) { push @both, [ $k, $v ]; } else { $exhausted = 1; $i = 0; return wantarray() ? () : undef(); } } else { ( $k, $v ) = @{$both[$i]}; } $i++; return wantarray() ? ( $k, $v ) : $k; } } } my %hashIteratorStore; sub hashIterator { my $h = shift; die "Not a hash reference!" unless ref $h eq 'HASH'; $hashIteratorStore{$h} //= hashIteratorFactory($h); return $hashIteratorStore{$h}->(); } my %h; @h{'a'..'d'} = 1..4; print \%h,"\n"; my %h2; @h2{'e'..'i'} = 5..8; my $i1 = hashIterator( \%h ); my $i2 = hashIterator( \%h ); print "1:",$i1->(),"\n"; print "1:",$i1->(),"\n"; print "2:",$i2->(),"\n"; print "1:",$i1->(),"\n"; print "1:",$i1->(),"\n"; my $i4 = hashIterator( \%h2 ); print "2:",$i2->(),"\n"; print "2:",$i2->(),"\n"; print "1:",$i1->(),"\n"; print "4:",$i4->(),"\n"; print "4:",$i4->(),"\n"; print "1:",$i1->(),"\n"; print "2:",$i2->(),"\n"; print "2:",$i2->(),"\n"; my $i3 = hashIterator( \%h ); while( my ($k, $v) = $i3->() ) { print "3:$k$v\n"; } print "2:",$i2->(),"\n"; print "1:",$i1->(),"\n";

        This is really only a starting point as it is really read-only. If the hash is changed somewhere inbetween, the cached keys and values will be unmodified. So more work is required.

Re: Indepedent lazy iterators for the same hash?
by Anonymous Monk on Jun 30, 2013 at 18:14 UTC

      Hash::StoredIterator - Functions for accessing a hashes internal iterator.

      Wooooot! That's great news (for me at the least). Thanks, Anony!

      Cheers, Sören

      (hooked on the Perl Programming language)