1Data::Rmap(3) User Contributed Perl Documentation Data::Rmap(3)
2
3
4
6 Data::Rmap - recursive map, apply a block to a data structure
7
9 $ perl -MData::Rmap -e 'print rmap { $_ } 1, [2,3], \\4, "\n"'
10 1234
11
12 $ perl -MData::Rmap=:all
13 rmap_all { print (ref($_) || "?") ,"\n" } \@array, \%hash, \*glob;
14
15 # OUTPUT (Note: a GLOB always has a SCALAR, hence the last two items)
16 # ARRAY
17 # HASH
18 # GLOB
19 # SCALAR
20 # ?
21
22
23 # Upper-case your leaves in-place
24 $array = [ "a", "b", "c" ];
25 $hash = { key => "a value" };
26 rmap { $_ = uc $_; } $array, $hash;
27
28 use Data::Dumper; $Data::Dumper::Terse=1; $Data::Dumper::Indent=0;
29 print Dumper($array), " ", Dumper($hash), "\n";
30
31 # OUTPUT
32 # ['A','B','C'] {'key' => 'A VALUE'}
33
34
35 # Simple array dumper.
36 # Uses $self->recurse method to alter traversal order
37 ($dump) = rmap_to {
38
39 return "'$_'" unless ref($_); # scalars are quoted and returned
40
41 my $self = shift;
42 # use $self->recurse to grab results and wrap them
43 return '[ ' . join(', ', $self->recurse() ) . ' ]';
44
45 } ARRAY|VALUE, [ 1, [ 2, [ [ 3 ], 4 ] ], 5 ];
46
47 print "$dump\n";
48 # OUTPUT
49 # [ '1', [ '2', [ [ '3' ], '4' ] ], '5' ]
50
52 rmap BLOCK LIST
53
54 Recursively evaluate a BLOCK over a list of data structures (locally
55 setting $_ to each element) and return the list composed of the results
56 of such evaluations. $_ can be used to modify the elements.
57
58 Data::Rmap currently traverses HASH, ARRAY, SCALAR and GLOB reference
59 types and ignores others. Depending on which rmap_* wrapper is used,
60 the BLOCK is called for only scalar values, arrays, hashes, references,
61 all elements or a customizable combination.
62
63 The list of data structures is traversed pre-order in a depth-first
64 fashion. That is, the BLOCK is called for the container reference
65 before is it called for it's elements (although see "recurse" below for
66 post-order). The values of a hash are traversed in the usual "values"
67 order which may affect some applications.
68
69 If the "cut" subroutine is called in the BLOCK then the traversal stops
70 for that branch, say if you "cut" an array then the code is never
71 called for it's elements (or their sub-elements). To simultaneously
72 return values and cut, simply pass the return list to cut:
73 "cut('add','to','returned');"
74
75 The first parameter to the BLOCK is an object which maintains the state
76 of the traversal. Methods available on this object are described in
77 "State Object" below.
78
80 By default:
81
82 rmap, rmap_all, cut
83
84 Optionally:
85
86 rmap_scalar rmap_hash rmap_array rmap_code rmap_ref rmap_to
87 :types => [ qw(NONE VALUE HASH ARRAY SCALAR REF CODE ALL) ],
88 :all => ... # everything
89
91 The various names are just wrappers which select when to call the code
92 BLOCK. rmap_all always calls it, the others are more selective while
93 rmap_to takes an extra parameter permitting you to provide selection
94 criteria. Furthermore, you can always just rmap_all and skip nodes
95 which are not of interest.
96
97 rmap_to { ... } $want, @data_structures;
98 Most general first.
99
100 Recurse the @data_structures and apply the BLOCK to elements
101 selected by $want. The $want parameter is the bitwise "or" of
102 whatever types you choose (imported with :types):
103
104 VALUE - non-reference scalar, eg. 1
105 HASH - hash reference
106 ARRAY - array reference
107 SCALAR - scalar refernce, eg. \1
108 REF - higher-level reference, eg. \\1, \\{}
109 B<NOT> any reference type, see <Scalar::Util>'s reftype:
110 perl -MScalar::Util=reftype -le 'print map reftype($_), \1, \\1'
111 GLOB - glob reference, eg. \*x
112 (scalar, hash and array recursed, code too as of 0.63)
113 ALL - all of the above (not CODE)
114 CODE - code references (as of 0.63)
115 NONE - none of the above
116
117 So to call the block for arrays and scalar values do:
118
119 use Data::Rmap ':all'; # or qw(:types rmap_to)
120 rmap { ... } ARRAY|VALUE, @data_structures;
121
122 (ALL | CODE) and (ALL & !GLOB) might also be handy.
123
124 The remainder of the wrappers are given in terms of the $want for
125 rmap_to.
126
127 rmap { ... } @list;
128 Recurse and call the BLOCK on non-reference scalar values. $want =
129 VALUE
130
131 rmap_all BLOCK LIST
132 Recurse and call the BLOCK on everything. $want = ALL
133
134 rmap_scalar { ... } @list
135 Recurse and call the BLOCK on non-collection scalars. $want =
136 VALUE|SCALAR|REF
137
138 rmap_hash
139 Recurse and call the BLOCK on hash refs. $want = HASH
140
141 rmap_array
142 Recurse and call the BLOCK on array refs. $want = ARRAY
143
144 rmap_code
145 Recurse and call the BLOCK on code refs. $want = CODE
146
147 rmap_ref
148 Recurse and call the BLOCK on all "normal" references: $want =
149 HASH|ARRAY|SCALAR|REF
150
151 Note: rmap_ref isn't the same as rmap_to {} REF
152
153 cut(@list)
154 Don't traverse sub-elements and return the @list immediately. For
155 example, if $_ is an ARRAY reference, then the array's elements are
156 not traversed.
157
158 If there's two paths to an element, both will need to be cut.
159
161 The first parameter to the BLOCK is an object which maintains most of
162 the traversal state (except current node, which is $_). You will
163 ignore it most of the time. The "recurse" method may be useful. Other
164 methods should only be used in throw away tools, see TODO
165
166 Methods:
167
168 recurse
169 Process child nodes of $_ now and return the result.
170
171 This makes it easier to perform post-order and in-order processing
172 of a structure. Note that since the same "seen list" is used, the
173 child nodes aren't reprocessed.
174
175 code
176 The code reference of the BLOCK itself. Possible useful in some
177 situations.
178
179 seen
180 Reference to the HASH used to track where we have visited. You may
181 want to modify it in some situations (though I haven't yet).
182 Beware circular references. The (current) convention used for the
183 key is in the source.
184
185 want
186 The $want state described in rmap_to.
187
189 # command-line play
190 $ perl -MData::Rmap -le 'print join ":", rmap { $_ } 1,2,[3..5],\\6'
191 1:2:3:4:5:6
192
193
194 # Linearly number questions on a set of pages
195 my $qnum = 1;
196 rmap_hash {
197 $_->{qnum} = $qnum++ if($_->{qn});
198 } @pages;
199
200
201 # Grep recursively, finding ALL objects
202 use Scalar::Util qw(blessed);
203 my @objects = rmap_ref {
204 blessed($_) ? $_ : ();
205 } $data_structure;
206
207
208 # Grep recursively, finding public objects (note the cut)
209 use Scalar::Util qw(blessed);
210 my @objects = rmap_ref {
211 blessed($_) ? cut($_) : ();
212 } $data_structure;
213
214
215 # Return a modified structure
216 # (result flattening means we must cheat by cloning then modifying)
217 use Storable qw(dclone);
218 use Lingua::EN::Numbers::Easy;
219
220 $words = [ 1, \2, { key => 3 } ];
221 $nums = dclone $words;
222 rmap { $_ = $N{$_} || $_ } $nums;
223
224
225 # Make an assertion about a structure
226 use Data::Dump;
227 rmap_ref {
228 blessed($_) && $_->isa('Question') && defined($_->name)
229 or die "Question doesn't have a name:", dump($_);
230 } @pages;
231
232
233 # Traverse a tree using localize state
234 $tree = [
235 one =>
236 two =>
237 [
238 three_one =>
239 three_two =>
240 [
241 three_three_one =>
242 ],
243 three_four =>
244 ],
245 four =>
246 [
247 [
248 five_one_one =>
249 ],
250 ],
251 ];
252
253 @path = ('q');
254 rmap_to {
255 if(ref $_) {
256 local(@path) = (@path, 1); # ARRAY adds a new level to the path
257 $_[0]->recurse(); # does stuff within local(@path)'s scope
258 } else {
259 print join('.', @path), " = $_ \n"; # show the scalar's path
260 }
261 $path[-1]++; # bump last element (even when it was an aref)
262 } ARRAY|VALUE, $tree;
263
264 # OUTPUT
265 # q.1 = one
266 # q.2 = two
267 # q.3.1 = three_one
268 # q.3.2 = three_two
269 # q.3.3.1 = three_three_one
270 # q.3.4 = three_four
271 # q.4 = four
272 # q.5.1.1 = five_one_one
273
274 # replace CODE with "<CODE>"
275 $ perl -MData::Rmap=:all -E 'say join ":", rmap_code { "<CODE>" } sub{},sub{}'
276 <CODE>:<CODE>
277
278 # look inside code refs with PadWalker
279 $ perl -MData::Rmap=:all -MSub::Identify=:all -MPadWalker=:all -MSub::Name
280 use 5.10.0;
281 my $s = sub {}; sub A::a { $s };
282 say join ", ",
283 rmap_code {
284 sub_fullname($_), # name string
285 map { $_[0]->recurse } closed_over($_) # then recurse the sub innards
286 } \*A::a, subname b => sub { $s };
287 # A::a, main::__ANON__, main::b
288
290 Beware comma after block:
291
292 rmap { print }, 1..3;
293 ^-------- bad news, you get an empty list:
294 rmap(sub { print $_; }), 1..3;
295
296 If you don't import a function, perl's confusion may produce:
297
298 $ perl -MData::Rmap -le 'rmap_scalar { print } 1'
299 Can't call method "rmap_scalar" without a package or object reference...
300
301 $ perl -MData::Rmap -le 'rmap_scalar { $_++ } 1'
302 Can't call method "rmap_scalar" without a package or object reference...
303
304 If there's two paths to an element, both will need to be cut.
305
306 If there's two paths to an element, one will be taken randomly when
307 there is an intervening hash.
308
309 Autovivification can lead to "Deep recursion" warnings if you test
310 "exists $_->{this}{that}" instead of "exists $_->{this} && exists
311 $_->{this}{that}" as you may follow a long chain of "this"s
312 Alternatively use the "no autovivification" pragma to avoid this
313 problem.
314
316 put for @_ in wrapper to allow parameters in a different wrapper, solve
317 localizing problem.
318
319 Store custom localized data about the traversal. Seems too difficult
320 and ugly when compare to doing it at the call site. Should support
321 multiple reentrancy so avoid the symbol table.
322
323 "rmap_args { } $data_structure, @args" form to pass parameters. Could
324 potentially help localizing needs. (Maybe only recurse last item)
325
326 Benchmark. Use array based object and/or direct access internally.
327
328 Think about permitting different callback for different types. The
329 prototype syntax is a bit too flaky....
330
331 Ensure that no memory leaks are possible, leaking the closure.
332
334 map, grep, Storable's dclone, Scalar::Util's reftype and blessed
335
336 Faint traces of treemap:
337
338 http://www.perlmonks.org/index.pl?node_id=60829
339
340 Update: various alternatives have appear over the years, Data::Visitor
341 has a list.
342
344 Brad Bowman <rmap@bereft.net>
345
347 Copyright (c) 2004- Brad Bowman (<rmap@bereft.net>). All rights
348 reserved.
349
350 This module is free software; you can redistribute it and/or modify it
351 under the same terms as Perl itself. See perlartistic and perlgpl.
352
353 This program is distributed in the hope that it will be useful, but
354 WITHOUT ANY WARRANTY; without even the implied warranty of
355 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
356
357
358
359perl v5.32.0 2020-07-28 Data::Rmap(3)