File: StoredIterator.pm

package info (click to toggle)
libhash-storediterator-perl 0.008-3
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, forky, sid, trixie
  • size: 304 kB
  • sloc: perl: 143; makefile: 3
file content (365 lines) | stat: -r--r--r-- 8,324 bytes parent folder | download | duplicates (3)
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.