Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Use Hash::StoredIterator to make %hash->each({...}) iterator safe

 * Safe to nest calls to each()
 * Safe to use keys() and values() inside each()
 * Does NOT copy all keys, uses internal iterator
  • Loading branch information...
commit 6a744ea2c145cb9e840ec5e123f0aad9891b0f23 1 parent 628fb1e
@exodist exodist authored
Showing with 75 additions and 9 deletions.
  1. +1 −0  Build.PL
  2. +2 −9 lib/perl5i/2/HASH.pm
  3. +72 −0 t/each.t
View
1  Build.PL
@@ -51,6 +51,7 @@ my $builder = MyBuild->new(
'Capture::Tiny' => '0.06',
'utf8::all' => '0.002',
'Carp::Fix::1_25' => '1.000000',
+ 'Hash::StoredIterator' => '0.001',
},
build_requires => {
'ExtUtils::CBuilder' => '0.26',
View
11 lib/perl5i/2/HASH.pm
@@ -7,19 +7,12 @@ use warnings;
# Don't accidentally turn carp/croak into methods.
require Carp::Fix::1_25;
+require Hash::StoredIterator;
use perl5i::2::Signatures;
method each($callback) {
- # Reset the each() iterator
- keys %$self;
-
- my($k,$v);
- while( ($k,$v) = CORE::each(%$self) ) {
- $callback->($k,$v);
- }
-
- return;
+ return Hash::StoredIterator::eech( $callback, $self );
}
sub flip {
View
72 t/each.t
@@ -45,4 +45,76 @@ note "each call is safe"; {
is_deeply \%have, \%want;
}
+note "Tests adapted from Hash::StoredIterator";
+
+my @want_outer = (
+ [a => 1],
+ [b => 2],
+ [c => 3],
+);
+
+my @want_inner = (
+ [a => 1],
+ [a => 1],
+ [a => 1],
+ [b => 2],
+ [b => 2],
+ [b => 2],
+ [c => 3],
+ [c => 3],
+ [c => 3],
+);
+
+my %hash = ( a => 1, b => 2, c => 3 );
+
+sub interference {
+ my @garbage = keys(%hash), values(%hash);
+ while ( my ( $k, $v ) = each(%hash) ) {
+ # Effectively do nothing
+ my $foo = $k . $v;
+ }
+};
+
+{
+ my @inner;
+ my @outer;
+
+ %hash->each( func( $k, $v ) {
+ ok( $k, "Got key" );
+ ok( $v, "Got val" );
+ is( $k, $_, '$_ is set to key' );
+ is( $k, $a, '$a is set to key' );
+ is( $v, $b, '$b is set to val' );
+
+ push @outer => [$k, $v];
+ interference();
+
+ %hash->each( func( $k2, $v2 ) {
+ is( $k2, $_, '$_ is set to key' );
+ is( $k2, $a, '$a is set to key' );
+ is( $v2, $b, '$b is set to val' );
+
+ push @inner => [$k, $v];
+
+ interference();
+ });
+
+ is( $k, $_, '$_ is not squashed by inner loop' );
+ is( $k, $a, '$a is not squashed by inner loop' );
+ is( $v, $b, '$a is not squashed by inner loop' );
+ });
+
+ is_deeply(
+ [sort { $a->[0] cmp $b->[0] } @outer],
+ \@want_outer,
+ "Outer loop got all keys"
+ );
+
+ is_deeply(
+ [sort { $a->[0] cmp $b->[0] } @inner],
+ \@want_inner,
+ "Inner loop got all keys multiple times"
+ );
+}
+
done_testing;
Please sign in to comment.
Something went wrong with that request. Please try again.