1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365
|
package Hash::StoredIterator;
use 5.010000;
use strict;
use warnings;
use base 'Exporter';
use Carp qw/croak carp/;
use B;
our @EXPORT_OK = qw{
hmap
eich
eech
iterator
hash_get_iterator
hash_set_iterator
hash_init_iterator
hkeys
hvalues
};
our %EXPORT_TAGS = (
'all' => \@EXPORT_OK
);
our $VERSION = '0.008';
require XSLoader;
XSLoader::load( 'Hash::StoredIterator', $VERSION );
sub eich(\%\$) {
carp "eich is deprecated, you should use iterator() instead";
my ( $hash, $i_ref ) = @_;
my $old_it = hash_get_iterator($hash);
my ( $key, $val );
my $success = eval {
if ( !defined $$i_ref )
{
hash_init_iterator($hash);
}
else {
hash_set_iterator( $hash, $$i_ref );
}
( $key, $val ) = each(%$hash);
$$i_ref = hash_get_iterator($hash);
1;
};
hash_set_iterator( $hash, $old_it );
die $@ unless $success;
unless ( defined $key ) {
$$i_ref = undef;
return;
}
return ( $key, $val );
}
sub iterator(\%) {
my ($hash) = @_;
my $i = undef;
return sub {
my $old_it = hash_get_iterator($hash);
my ( $key, $val );
my $success = eval {
if ( !defined $i ) {
hash_init_iterator( $hash );
}
else {
hash_set_iterator( $hash, $i );
}
( $key, $val ) = each( %$hash );
$i = hash_get_iterator($hash);
1;
};
hash_set_iterator( $hash, $old_it );
die $@ unless $success;
unless ( defined $key ) {
$i = undef;
return;
}
return ( $key, $val );
};
}
sub hmap(&\%) {
my ( $code, $hash ) = @_;
my $old_it = hash_get_iterator($hash);
hash_init_iterator($hash);
my $success = eval {
my $iter = iterator %$hash;
while ( my ( $k, $v ) = $iter->() ) {
local $_ = $k;
# Can't use caller(), subref might be from a different package than
# eech is called from.
my $callback_package = B::svref_2object($code)->GV->STASH->NAME;
no strict 'refs';
local ${"$callback_package\::a"} = $k;
local ${"$callback_package\::b"} = $v;
$code->( $k, $v );
}
1;
};
hash_set_iterator( $hash, $old_it );
die $@ unless $success;
return;
}
sub eech(&\%) {
carp "eech is deprecated, use hmap instead";
goto &hmap;
}
sub hkeys(\%) {
my ($hash) = @_;
croak "ARGH!" unless $hash;
my $old_it = hash_get_iterator($hash);
hash_init_iterator($hash);
my @out = keys %$hash;
hash_set_iterator( $hash, $old_it );
return @out;
}
sub hvalues(\%) {
my ($hash) = @_;
my $old_it = hash_get_iterator($hash);
hash_init_iterator($hash);
my @out = values %$hash;
hash_set_iterator( $hash, $old_it );
return @out;
}
1;
__END__
=head1 NAME
Hash::StoredIterator - Functions for accessing a hashes internal iterator.
=head1 DESCRIPTION
In perl all hashes have an internal iterator. This iterator is used by the
C<each()> function, as well as by C<keys()> and C<values()>. Because these all
share use of the same iterator, they tend to interact badly with each other
when nested.
Hash::StoredIterator gives you access to get, set, and init the iterator inside
a hash. This allows you to store the current iterator, use
each/keys/values/etc, and then restore the iterator, this helps you to ensure
you do not interact badly with other users of the iterator.
Along with low-level get/set/init functions, there are also 2 variations of
C<each()> which let you act upon each key/value pair in a safer way than
vanilla C<each()>
This module can also export new implementations of C<keys()> and C<values()>
which stash and restore the iterator so that they are safe to use within
C<each()>.
=head1 SYNOPSIS
use Hash::StoredIterator qw{
hmap
hkeys
hvalues
iterator
hash_get_iterator
hash_set_iterator
hash_init_iterator
};
my %hash = map { $_ => uc( $_ )} 'a' .. 'z';
my @keys = hkeys %hash;
my @values = hvalues %hash;
Each section below is functionally identical.
my $iterator = iterator %hash;
while( my ( $k, $v ) = $i->() ) {
print "$k: $value\n";
}
hmap { print "$a: $b\n" } %hash;
hamp { print "$_: $b\n" } %hash;
hmap {
my ( $key, $val ) = @_;
print "$key: $val\n";
} %hash;
It is safe to nest calls to C<hmap()>, C<iterator()>, C<hkeys()>, and C<hvalues()>
hmap {
my ( $key, $val ) = @_;
print "$key: $val\n";
my @keys = hkeys( %hash );
} %hash;
C<hmap()> and C<iterator()> will also properly handle calls to C<CORE::each>,
C<CORE::keys>, and C<Core::values> nested within them.
hmap {
my ( $key, $val ) = @_;
print "$key: $val\n";
# No infinite loop!
my @keys = keys %hash;
} %hash;
Low Level:
hash_init_iterator( \%hash );
my $iter = hash_get_iterator( \%hash );
# NOTE: Never manually specify an $iter value, ALWAYS use a value from
# hash_get_iterator.
hash_set_iterator( \%hash, $iter );
=head1 EXPORTS
=over 4
=item my $i = iterator %hash
Get an iterator that can be used to retrieve key/value pairs.
my $i = iterator %hash;
while( my ($k, $v) = $i->() ) {
...
}
The iterator is a coderef, so you call it like this: C<$i->()>. You can also
use the sub anywhere you would use any other coderef.
=item hmap( \&callback, %hash )
=item hmap { ... } %hash
Iterate each key/pair calling C<$callback->( $key, $value )> for each set. In
addition C<$a> and C<$_> are set to the key, and C<$b> is set to the value.
This is done primarily for convenience of matching against the key, and short
callbacks that will be cluttered by parsing C<@_> noise.
B<Note:> See caveats.
=item my @keys = hkeys( %hash )
Same as the builtin C<keys()>, except it stores and restores the iterator.
B<Note:> Overriding the builtin keys(), even locally, causes strange
interactions with other builtins. When trying to export hkeys as keys, a call
to C<sort keys %hash> would cause undef to be passed into keys() as the first
and only argument.
=item my @values = hvalues( %hash )
Same as the builtin C<values()>, except it stores and restores the iterator.
B<Note:> Overriding the builtin values(), even locally, causes strange
interactions with other builtins. When trying to export hvalues as values, a
call to C<sort values %hash> would cause undef to be passed into values() as
the first and only argument.
=item my $i = hash_get_iterator( \%hash )
Get the current iterator value.
=item hash_set_iterator( \%hash, $i )
Set the iterator value.
B<Note:> Only ever set this to the value retrieved by C<hash_get_iterator()>,
setting the iterator in any other way is untested, and may result in undefined
behavior.
=item hash_init_iterator( \%hash )
Initialize or reset the hash iterator.
=back
=head1 DEPRECATED
These have been deprecated because they were terrible names. eich was also
deprecated because it was unnatural to use.
=over 4
=item eich
use iterator() instead
=item eech
use hmap instead
=back
=head1 CAVEATS
=over 4
=item Modification of hash during iteration
Just like with the builtin C<each()> modifying the hash between calls to each
is not recommended and can result in undefined behavior. The builtin C<each()>
does allow for deleting the iterations key, however that is B<NOT> supported by
this library.
=item sort() edge case
For some reason C<[sort hkeys %hash]> and C<[sort hkeys(%hash)]> both result in
a list that has all the keys and values (and strangely not in sorted order).
However C<[sort(hkeys(%hash))]> works fine.
=back
=head1 AUTHORS
Chad Granum L<exodist7@gmail.com>
=head1 COPYRIGHT
Copyright (C) 2013 Chad Granum
Hash-StoredIterator is free software; Standard perl licence.
Hash-StoredIterator is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the license for more details.
|