The following snippet creates a class: Tie::Hash::PEach which allows you to set the starting point for each iterating. The starting point is set in terms of a zero-based index.
Hashes don't carry any predictable order, but unless a hash changes its keys, order does remain stable. So if you know the order and care about the point at which you begin your iterations, this snippet will allow for custom entry points.
The other primary goal of this snippet is a proof-of-concept to demonstrate (mostly to myself) how one might go about providing for a localizable 'each' iterator. The idea here is that if you localize the instance variable $obj->[1]{ITER}, you can have two separate 'each' iterator mechanisms going on, each in their own scope, without interfering with each other. This seems to be of more use than the ability to set the 'each' index. I wanted to figure out a way to allow an 'each' call in one sub's scope to act independantly of an 'each' call for the same hash in another sub's scope. This snippet accomplishes that.
I'm interested in any comments. This was intended to be a learning experience for me.
The snippet below includes a test to demonstrate the concept of localized each iterators. The class methods contain some unnecessarily explicit constructs just to make things clear. (Clearer than they appear in the Tie::Hash module.)
Update: Just wanted to credit tye for thinking of what to name the class, and Limbic~Region for getting me thinking about this topic.
package Tie::Hash::PEach; use strict; use warnings; sub FIRSTKEY { my( $self ) = shift; my( %hash ) = %{ $self->[0] }; my( $iterator ) = $self->[1]{ITER}; my( @keys ) = keys %hash; $iterator = 0 unless defined $iterator; my( @pair ) = ( $keys[ $iterator ], $hash{ $keys[ $iterator ] } ); $iterator = ( $iterator < $#keys ) ? $iterator + 1 : undef; $self->[1]{ITER} = $iterator; @{$self->[1]{KEYS}} = @keys; return wantarray ? @pair : $pair[0]; } sub NEXTKEY { my( $self ) = shift; my( %hash ) = %{ $self->[0] }; my( $iterator ) = $self->[1]{ITER}; my( @keys ) = @{ $self->[1]{KEYS} }; $iterator = 0 unless defined $iterator; my( @pair ) = ( $keys[ $iterator ], $hash{ $keys[ $iterator ] } ); if ( $iterator < $#keys ) { $iterator++; $self->[1]{ITER} = $iterator; return wantarray ? @pair : $pair[0]; } else { $iterator = undef; $self->[1]{ITER} = $iterator; return undef; } } sub TIEHASH { bless [ {}, { ITER => undef, KEYS => [] } ], shift; } sub STORE { my( $self, $key, $value ) = @_; ${$self->[0]}{$key} = $value; } sub FETCH { my( $self, $key ) = @_; return ${$self->[0]}{$key}; } sub EXISTS { my( $self, $key ) = @_; return exists ${$self->[0]}{$key}; } sub DELETE { my( $self, $key ) = @_; delete ${$self->[0]}{$key}; } sub CLEAR { my $self = shift; %{$self->[0]} = (); } sub SCALAR { my $self = shift; scalar %{$self->[0]}; } 1; package main; use strict; use warnings; use Data::Dumper; my $obj = tie my %hash, "Tie::Hash::PEach"; %hash = qw/one 1 two 2 three 3 four 4 five 5 six 6/; print "\n\nThe order of \%hash:\n"; print Dumper \%hash; { print "\n\nLocalized 'each' instance iterator for \%hash:\n"; # To change 'each' iterator for %hash, set $obj->[1]{ITER}. # Remember, the first element is 0. local $obj->[1]{ITER} = 2; my ( $key, $value ) = each %hash; print "$key => $value\n"; } print "\n\nNonlocalized 'each' instance iterator for \%hash:\n"; my ( $key, $value ) = each %hash; print "$key => $value\n";
In reply to Localizable / customizable 'each' iterator for hashes by davido
| For: | Use: | ||
| & | & | ||
| < | < | ||
| > | > | ||
| [ | [ | ||
| ] | ] |