casegen 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/bin/casegen +11 -0
- data/src/agents/sets.rb +336 -0
- data/src/agents/sets/enum/by.rb +244 -0
- data/src/agents/sets/enum/cluster.rb +164 -0
- data/src/agents/sets/enum/inject.rb +50 -0
- data/src/agents/sets/enum/install.rb +73 -0
- data/src/agents/sets/enum/nest.rb +117 -0
- data/src/agents/sets/enum/op.rb +283 -0
- data/src/agents/sets/enum/pipe.rb +160 -0
- data/src/agents/sets/enum/tree.rb +442 -0
- data/src/calc.sample.txt +13 -0
- data/src/cart.sample.txt +33 -0
- data/src/casegen.rb +191 -0
- data/src/ruby_array.sample.txt +20 -0
- metadata +66 -0
@@ -0,0 +1,160 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
module Enumerable
|
4
|
+
|
5
|
+
class Pipe
|
6
|
+
include Enumerable
|
7
|
+
|
8
|
+
attr_reader :enum, :filter_name, :filter_map, :args
|
9
|
+
|
10
|
+
def initialize enum, filter_spec = nil, *args, &filter_proc
|
11
|
+
@enum = enum
|
12
|
+
@args = args
|
13
|
+
|
14
|
+
case filter_spec
|
15
|
+
when Symbol
|
16
|
+
@filter_name = filter_spec
|
17
|
+
when String
|
18
|
+
@filter_name = filter_spec.intern
|
19
|
+
when nil
|
20
|
+
@filter_map = filter_proc
|
21
|
+
else
|
22
|
+
unless filter_spec.respond_to? :[]
|
23
|
+
raise ArgumentError,
|
24
|
+
"filter_spec must be a method name or respond to []."
|
25
|
+
end
|
26
|
+
@filter_map = filter_spec
|
27
|
+
end
|
28
|
+
|
29
|
+
unless @filter_name or @filter_map
|
30
|
+
raise ArgumentError,
|
31
|
+
"no filter specified."
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def each
|
36
|
+
if @filter_name
|
37
|
+
filter_name = @filter_name
|
38
|
+
message = filter_name, *@args
|
39
|
+
@enum.each { |entry|
|
40
|
+
yield entry.send *message
|
41
|
+
}
|
42
|
+
elsif @filter_map
|
43
|
+
filter_map = @filter_map
|
44
|
+
args = *@args
|
45
|
+
@enum.each { |entry|
|
46
|
+
yield filter_map[entry, *args]
|
47
|
+
}
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
self
|
52
|
+
end
|
53
|
+
|
54
|
+
def pipe filter_spec = nil, *args, &filter_proc
|
55
|
+
Pipe.new self, filter_spec, *args, &filter_proc
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
|
60
|
+
|
61
|
+
=begin
|
62
|
+
|
63
|
+
==class Enumerable
|
64
|
+
===instance method
|
65
|
+
---Enumerable#pipe filter_spec = nil, *args, &filter_proc
|
66
|
+
|
67
|
+
Can be used to "pipe" an (({Enumerable})) sequence through a filter.
|
68
|
+
|
69
|
+
(({Enumerable#pipe})) returns an (({Enumerable})) object whose (({each}))
|
70
|
+
method iterates over (({self})) and applies a filter to each enumerated
|
71
|
+
object, as specified by the arguments. Only the current element of the
|
72
|
+
sequence is kept in memory.
|
73
|
+
|
74
|
+
If (({filter_spec})) is a string or symbol, (({filter_proc})) is ignored
|
75
|
+
and (({filter_spec})) is treated as a method name. This method name is
|
76
|
+
sent, along with arguments (({args})), to each element of the sequence
|
77
|
+
being enumerated.
|
78
|
+
|
79
|
+
If (({filter_spec})) is anything else, except (({nil})), (({filter_proc}))
|
80
|
+
is ignored and (({filter_spec})) is required to be an object that responds
|
81
|
+
to (({[]})), such as a proc or a hash. The (({[]})) method of
|
82
|
+
(({filter_spec})) is called with each element of the sequence in turn as
|
83
|
+
an argument, along with (({args})).
|
84
|
+
|
85
|
+
If (({next_spec})) is not given, or is (({nil})), a block is required. In
|
86
|
+
this case, iteration proceeds as in the preceding paragraph.
|
87
|
+
|
88
|
+
Using (({#pipe})) has potential performance advantages. The iteration
|
89
|
+
|
90
|
+
e.collect { |x| x.m }.each { |y| ... }
|
91
|
+
|
92
|
+
can be rewritten as
|
93
|
+
|
94
|
+
e.pipe(:m).each { |y| ... }
|
95
|
+
|
96
|
+
which doesn't generate an intermediate array, and uses a send instead of a
|
97
|
+
proc call. Of course, it could also be written as
|
98
|
+
|
99
|
+
e.each { |x| y = x.m ... }
|
100
|
+
|
101
|
+
but that may be undesirable for readability or because the block is to be
|
102
|
+
taken from a proc contained in a variable:
|
103
|
+
|
104
|
+
pr = proc { ... }
|
105
|
+
e.pipe(:m).each &pr
|
106
|
+
|
107
|
+
Also, chains of (({collect})) and (({select})), such as
|
108
|
+
|
109
|
+
(1..100).collect { |x| x**2 }.select { |y| y > 1000 && y < 2000 }
|
110
|
+
|
111
|
+
can't be easily rewritten as a single (({select})).
|
112
|
+
|
113
|
+
===examples
|
114
|
+
|
115
|
+
require 'enum/pipe'
|
116
|
+
|
117
|
+
[0,1,2,3,4].pipe { |x| x + 1 }.each { |x|
|
118
|
+
print x, " "
|
119
|
+
}
|
120
|
+
|
121
|
+
# prints: 1 2 3 4 5
|
122
|
+
|
123
|
+
stooges = ['lARRY', 'cURLY', 'mOE']
|
124
|
+
p stooges.pipe(:swapcase).reject { |x| x =~ /url/ }
|
125
|
+
p stooges.pipe(:tr, 'RlcOEL', 'gBboog').pipe(:capitalize).entries
|
126
|
+
|
127
|
+
# prints: ["Larry", "Moe"]
|
128
|
+
# ["Baggy", "Buggy", "Moo"]
|
129
|
+
|
130
|
+
==version
|
131
|
+
|
132
|
+
Enumerable tools 1.6
|
133
|
+
|
134
|
+
The current version of this software can be found at
|
135
|
+
((<"http://redshift.sourceforge.net/enum
|
136
|
+
"|URL:http://redshift.sourceforge.net/enum>)).
|
137
|
+
|
138
|
+
==license
|
139
|
+
This software is distributed under the Ruby license.
|
140
|
+
See ((<"http://www.ruby-lang.org"|URL:http://www.ruby-lang.org>)).
|
141
|
+
|
142
|
+
==author
|
143
|
+
Joel VanderWerf,
|
144
|
+
((<vjoel@users.sourceforge.net|URL:mailto:vjoel@users.sourceforge.net>))
|
145
|
+
|
146
|
+
=end
|
147
|
+
|
148
|
+
|
149
|
+
if __FILE__ == $0
|
150
|
+
|
151
|
+
[0,1,2,3,4].pipe { |x| x + 1 }.each { |x|
|
152
|
+
print x, " "
|
153
|
+
}
|
154
|
+
puts
|
155
|
+
|
156
|
+
stooges = ['lARRY', 'cURLY', 'mOE']
|
157
|
+
p stooges.pipe(:swapcase).reject { |x| x =~ /url/ }
|
158
|
+
p stooges.pipe(:tr, 'RlcOEL', 'gBboog').pipe(:capitalize).entries
|
159
|
+
|
160
|
+
end
|
@@ -0,0 +1,442 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
module Enumerable
|
4
|
+
|
5
|
+
class TreeDelegator
|
6
|
+
include Enumerable
|
7
|
+
|
8
|
+
attr_reader :root, :child_name, :child_map,
|
9
|
+
:args, :output_type
|
10
|
+
|
11
|
+
def initialize root, child_spec = nil, *args, &child_proc
|
12
|
+
@root = root
|
13
|
+
@args = args
|
14
|
+
@output_type = :nodes
|
15
|
+
|
16
|
+
case child_spec
|
17
|
+
when Symbol
|
18
|
+
@child_name = child_spec
|
19
|
+
when String
|
20
|
+
@child_name = child_spec.intern
|
21
|
+
when nil
|
22
|
+
@child_map = child_proc
|
23
|
+
else
|
24
|
+
unless child_spec.respond_to? :[]
|
25
|
+
raise ArgumentError,
|
26
|
+
"child_spec must be a method name or respond to []."
|
27
|
+
end
|
28
|
+
@child_map = child_spec
|
29
|
+
end
|
30
|
+
|
31
|
+
unless @child_name or @child_map
|
32
|
+
raise ArgumentError,
|
33
|
+
"no child-getter specified."
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def get_children cur
|
38
|
+
(if @child_name
|
39
|
+
cur.send @child_name, *@args
|
40
|
+
else
|
41
|
+
@child_map[cur, *@args]
|
42
|
+
end).to_a
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
|
47
|
+
class ByBreadthDelegator < TreeDelegator
|
48
|
+
|
49
|
+
def each level = [@root], &block
|
50
|
+
|
51
|
+
children = []
|
52
|
+
for node in level
|
53
|
+
yield node
|
54
|
+
children |= get_children(node) || []
|
55
|
+
end
|
56
|
+
|
57
|
+
if children and not children.empty?
|
58
|
+
each children, &block
|
59
|
+
end
|
60
|
+
|
61
|
+
self
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
|
66
|
+
class ByDepthDelegator < TreeDelegator
|
67
|
+
|
68
|
+
def with_ancestors
|
69
|
+
@output_type = :with_ancestors
|
70
|
+
self
|
71
|
+
end
|
72
|
+
|
73
|
+
def branches
|
74
|
+
@output_type = :branches
|
75
|
+
self
|
76
|
+
end
|
77
|
+
|
78
|
+
def each(*args, &block)
|
79
|
+
case @output_type
|
80
|
+
when :nodes
|
81
|
+
each_node *args, &block
|
82
|
+
when :with_ancestors
|
83
|
+
each_with_ancestors *args, &block
|
84
|
+
when :branches
|
85
|
+
each_branch *args, &block
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
protected
|
90
|
+
def each_node cur = @root, &block
|
91
|
+
|
92
|
+
result = catch (:prune) do
|
93
|
+
yield cur
|
94
|
+
|
95
|
+
children = get_children cur
|
96
|
+
|
97
|
+
if children
|
98
|
+
for child in children
|
99
|
+
each_node child, &block
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
false
|
104
|
+
end
|
105
|
+
|
106
|
+
if result and result > 0
|
107
|
+
throw :prune, result - 1
|
108
|
+
end
|
109
|
+
|
110
|
+
self
|
111
|
+
end
|
112
|
+
|
113
|
+
def each_with_ancestors cur = @root, ancestors = [], &block
|
114
|
+
|
115
|
+
result = catch (:prune) do
|
116
|
+
yield cur, ancestors
|
117
|
+
|
118
|
+
children = get_children cur
|
119
|
+
|
120
|
+
if children
|
121
|
+
for child in children
|
122
|
+
each_with_ancestors child, ancestors + [cur], &block
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
false
|
127
|
+
end
|
128
|
+
|
129
|
+
if result and result > 0
|
130
|
+
throw :prune, result - 1
|
131
|
+
end
|
132
|
+
|
133
|
+
self
|
134
|
+
end
|
135
|
+
|
136
|
+
def each_branch branch = [@root], &block
|
137
|
+
cur = branch[-1]
|
138
|
+
|
139
|
+
children = get_children cur
|
140
|
+
|
141
|
+
if children and not children.empty?
|
142
|
+
for child in children
|
143
|
+
each_branch branch + [child], &block
|
144
|
+
end
|
145
|
+
else
|
146
|
+
yield branch
|
147
|
+
end
|
148
|
+
|
149
|
+
self
|
150
|
+
end
|
151
|
+
|
152
|
+
end
|
153
|
+
|
154
|
+
end
|
155
|
+
|
156
|
+
class Object
|
157
|
+
def by_depth child_spec = nil, *args, &child_proc
|
158
|
+
Enumerable::ByDepthDelegator.new self, child_spec, *args, &child_proc
|
159
|
+
end
|
160
|
+
def by_breadth child_spec = nil, *args, &child_proc
|
161
|
+
Enumerable::ByBreadthDelegator.new self, child_spec, *args, &child_proc
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
|
166
|
+
=begin
|
167
|
+
|
168
|
+
==class Object
|
169
|
+
===instance methods
|
170
|
+
---Object#by_depth child_spec = nil, *args, &child_proc
|
171
|
+
---Object#by_breadth child_spec = nil, *args, &child_proc
|
172
|
+
|
173
|
+
Allows use of (({Enumerable})) methods, such as (({each})), (({collect})),
|
174
|
+
(({select})), etc., to iterate over objects which have a caller-specified tree
|
175
|
+
structure (or, more generally, directed acyclic graph structure). The caller
|
176
|
+
defines this structure by providing a way of calculating the children of each
|
177
|
+
object, such as an accessor method for the array of children of a node in a
|
178
|
+
tree.
|
179
|
+
|
180
|
+
In order to yield a sequence of objects, the nonlinear structure of the tree is
|
181
|
+
linearized in either a depth-first or a breadth-first way, depending on the
|
182
|
+
method used, (({by_depth})) or (({by_breadth})). In a depth-first
|
183
|
+
linearization, the iteration visits a node's children before continuing with
|
184
|
+
the node's successive siblings. In a breadth-first linearization, the iteration
|
185
|
+
visits all siblings before visiting any of their children. In either case, the
|
186
|
+
parent is visited before its children, and the children are visited in an order
|
187
|
+
consistent with their order in the sequence of children provided by their
|
188
|
+
parent.
|
189
|
+
|
190
|
+
Speaking loosely, one can think of a depth-first iteration as working branch by
|
191
|
+
branch and a breadth-first iteration as working level by level, where a level
|
192
|
+
consists of nodes of equal depth.
|
193
|
+
|
194
|
+
====usage
|
195
|
+
|
196
|
+
require 'enum/tree'
|
197
|
+
|
198
|
+
for node in root.by_depth :a_method, ...
|
199
|
+
|
200
|
+
for node in root.by_depth a_proc, ...
|
201
|
+
|
202
|
+
for node in root.by_depth { |node| ... return children }
|
203
|
+
|
204
|
+
# by_breadth has the same form
|
205
|
+
|
206
|
+
====arguments
|
207
|
+
|
208
|
+
The means of accessing the children of each node is specified in the
|
209
|
+
argument list with a method name, a block, or an object that responds to
|
210
|
+
(({[]})), such as a proc or a hash. In any case, the value returned must be an
|
211
|
+
(({Enumerable})) object, typically an array.
|
212
|
+
|
213
|
+
If (({child_spec})) is a string or symbol, (({child_proc})) is ignored and
|
214
|
+
(({child_spec})) is treated as a method name. This method name is sent, along
|
215
|
+
with arguments (({args})), to each node to generate the children. The node is
|
216
|
+
considered childless when the method returns (({nil})) or (({false})) or an empty collection.
|
217
|
+
|
218
|
+
If (({child_spec})) is anything else, except (({nil})), (({child_proc})) is
|
219
|
+
ignored and (({child_spec})) is required to be an object that responds to
|
220
|
+
(({[]})), such as a proc or a hash. The (({[]})) method of (({child_spec})) is
|
221
|
+
called with each node as an argument, along with (({args})), to generate the
|
222
|
+
children. The node is considered childless when (({[]})) returns (({nil})) or
|
223
|
+
(({false})) or an empty collection.
|
224
|
+
|
225
|
+
If (({child_spec})) is not given, or is (({nil})), a block is required. In this
|
226
|
+
case, the block is converted to a proc and iteration proceeds as in the
|
227
|
+
preceding paragraph.
|
228
|
+
|
229
|
+
====return value
|
230
|
+
|
231
|
+
The return value is not an array, but an (({Enumerable})) object that refers
|
232
|
+
to the original objects. In this sense, (({Object#by_depth})) and
|
233
|
+
(({Object#by_breadth})) are ((*delegators*)). Typically, (({by_depth})) and
|
234
|
+
(({by_breadth})) are used with the (({for ... in ...})) construct, or
|
235
|
+
(equivalently) with (({each})), or with (({collect})), (({select})), and so on.
|
236
|
+
In these cases, the dependence on the original data structure does not matter.
|
237
|
+
To get the array of entries produced by (({by_depth})) or (({by_breadth})) as
|
238
|
+
an independent data structure, use (({Enumerable#entries})) or
|
239
|
+
(({Enumerable#to_a})).
|
240
|
+
|
241
|
+
====directed acyclic graphs
|
242
|
+
|
243
|
+
If a node occurs as the child of two different parent nodes, the structure is
|
244
|
+
not a tree. As long as no node is its own ancestor, these methods still produce
|
245
|
+
a useful interation (it is the caller's responsibility to avoid cycles). The
|
246
|
+
structure in this case is sometimes called a ((*directed acyclic graph*)). In a
|
247
|
+
depth-first iteration, a node with two parents will be reached twice. In a
|
248
|
+
breadth-first iteration, a node with two parents will be reaced once if the
|
249
|
+
parents are at the same depth in the graph, but twice otherwise.
|
250
|
+
|
251
|
+
====modifiers
|
252
|
+
|
253
|
+
The (({Object#by_depth})) method accepts two modifiers that affect the yielded
|
254
|
+
values, but not the order of iteration:
|
255
|
+
|
256
|
+
for node, ancestors in tree.by_depth(...).with_ancestors
|
257
|
+
|
258
|
+
for branch in tree.by_depth(...).branches
|
259
|
+
|
260
|
+
The (({with_ancestors})) modifier results in the same linearization, but
|
261
|
+
returns, along with each node, the node's array of ancestors, starting with the
|
262
|
+
root of the tree and ending with the immediate parent. In the non-tree case, the
|
263
|
+
ancestor list doesn't contain all ancestors, but just one path from the root to
|
264
|
+
the node. Each such path will occur once during the (({by_depth})) iteration.
|
265
|
+
|
266
|
+
With the (({branches})) modifier, the iteration yields all branches of the tree
|
267
|
+
(or directed acyclic graph). A ((*branch*)) is a path from the root to a leaf
|
268
|
+
node.
|
269
|
+
|
270
|
+
Note that a (({with_ancestors})) iteration yields at every node, but a
|
271
|
+
(({branches})) iteration yields only at leaf nodes.
|
272
|
+
|
273
|
+
====prune
|
274
|
+
|
275
|
+
Pruning the iteration means skipping a node and its descendants and continuing
|
276
|
+
with the nodes that would normally follow them in the iteration. This can be
|
277
|
+
done anywhere in dynamic scope during the iteration by simply throwing the
|
278
|
+
(({:prune})) symbol:
|
279
|
+
|
280
|
+
throw :prune
|
281
|
+
|
282
|
+
If an additional integer argument is supplied:
|
283
|
+
|
284
|
+
throw :prune, n
|
285
|
+
|
286
|
+
then the pruning occurs not at the current node, but at the node (({n})) levels
|
287
|
+
above the current node in the current ancestor list.
|
288
|
+
|
289
|
+
Note that (({prune})) cannot used with the (({each_branch})) modifier discussed
|
290
|
+
above; (({prune})) simply has no useful meaning in this context.
|
291
|
+
|
292
|
+
====examples
|
293
|
+
|
294
|
+
require 'enum/tree'
|
295
|
+
|
296
|
+
# Define a proc to compute the subclasses of a class
|
297
|
+
# It would probably be better to make this a method of Class
|
298
|
+
# and to implement it more efficiently, but for illustration...
|
299
|
+
|
300
|
+
subclasses = proc { |cl|
|
301
|
+
subs = []
|
302
|
+
ObjectSpace.each_object(Class) { |sub|
|
303
|
+
if sub.superclass == cl then subs << sub end
|
304
|
+
}
|
305
|
+
subs
|
306
|
+
}
|
307
|
+
|
308
|
+
print "Subclasses of Numeric:\n"
|
309
|
+
for node in Numeric.by_depth subclasses
|
310
|
+
print node, " "
|
311
|
+
end
|
312
|
+
|
313
|
+
# prints:
|
314
|
+
# Subclasses of Numeric:
|
315
|
+
# Numeric Float Integer Bignum Fixnum
|
316
|
+
|
317
|
+
puts "\n\nBranches:"
|
318
|
+
for branch in Numeric.by_depth(subclasses).branches
|
319
|
+
p branch
|
320
|
+
end
|
321
|
+
|
322
|
+
# prints:
|
323
|
+
# Branches:
|
324
|
+
# [Numeric, Float]
|
325
|
+
# [Numeric, Integer, Bignum]
|
326
|
+
# [Numeric, Integer, Fixnum]
|
327
|
+
|
328
|
+
puts "\nNodes with ancestors:"
|
329
|
+
for x, a in Numeric.by_depth(subclasses).with_ancestors
|
330
|
+
print "\t"*a.size, "node is #{x}, ancestors is #{a.inspect}.\n"
|
331
|
+
end
|
332
|
+
|
333
|
+
# prints:
|
334
|
+
# Nodes with ancestors:
|
335
|
+
# node is Numeric, ancestors is [].
|
336
|
+
# node is Float, ancestors is [Numeric].
|
337
|
+
# node is Integer, ancestors is [Numeric].
|
338
|
+
# node is Bignum, ancestors is [Numeric, Integer].
|
339
|
+
# node is Fixnum, ancestors is [Numeric, Integer].
|
340
|
+
|
341
|
+
See the end of the source file for more examples.
|
342
|
+
|
343
|
+
===version
|
344
|
+
|
345
|
+
Enumerable tools 1.6
|
346
|
+
|
347
|
+
The current version of this software can be found at
|
348
|
+
((<"http://redshift.sourceforge.net/enum
|
349
|
+
"|URL:http://redshift.sourceforge.net/enum>)).
|
350
|
+
|
351
|
+
===license
|
352
|
+
This software is distributed under the Ruby license.
|
353
|
+
See ((<"http://www.ruby-lang.org"|URL:http://www.ruby-lang.org>)).
|
354
|
+
|
355
|
+
===author
|
356
|
+
Joel VanderWerf,
|
357
|
+
((<vjoel@users.sourceforge.net|URL:mailto:vjoel@users.sourceforge.net>))
|
358
|
+
|
359
|
+
=end
|
360
|
+
|
361
|
+
|
362
|
+
if __FILE__ == $0
|
363
|
+
|
364
|
+
tree = [ [1,2,[3]], [4, [], 5] ]
|
365
|
+
for x in tree.by_depth { |x| x.kind_of?(Array) ? x : [] }
|
366
|
+
p x
|
367
|
+
end
|
368
|
+
puts
|
369
|
+
|
370
|
+
class Node
|
371
|
+
|
372
|
+
attr_reader :value, :children
|
373
|
+
|
374
|
+
def initialize value, children = nil
|
375
|
+
@value = value
|
376
|
+
@children = children
|
377
|
+
unless not children or children.respond_to? :each
|
378
|
+
raise ArgumentError, "children must be nil or have an each method."
|
379
|
+
end
|
380
|
+
end
|
381
|
+
|
382
|
+
end
|
383
|
+
|
384
|
+
tree = Node.new(0,
|
385
|
+
[Node.new(1,
|
386
|
+
[Node.new(2,
|
387
|
+
[Node.new(3)]),
|
388
|
+
Node.new(4),
|
389
|
+
Node.new(5)]),
|
390
|
+
Node.new(6)])
|
391
|
+
|
392
|
+
for node in tree.by_depth :children
|
393
|
+
if node.value == 4
|
394
|
+
throw :prune, 1
|
395
|
+
end
|
396
|
+
print node.value
|
397
|
+
end
|
398
|
+
puts
|
399
|
+
|
400
|
+
for node in tree.by_breadth :children
|
401
|
+
print node.value
|
402
|
+
end
|
403
|
+
puts
|
404
|
+
|
405
|
+
# Define a proc to compute the subclasses of a Class
|
406
|
+
# It would probably be better to make this a method of Class
|
407
|
+
# and to implement it more efficiently, but for illustration...
|
408
|
+
subclasses = proc { |cl|
|
409
|
+
subs = []
|
410
|
+
ObjectSpace.each_object(Class) { |sub|
|
411
|
+
if sub.superclass == cl then subs << sub end
|
412
|
+
}
|
413
|
+
subs
|
414
|
+
}
|
415
|
+
|
416
|
+
print "\nSubclasses of Numeric:\n"
|
417
|
+
for node in Numeric.by_depth subclasses
|
418
|
+
print node, " "
|
419
|
+
end
|
420
|
+
print "\n\n"
|
421
|
+
|
422
|
+
puts "Branches:"
|
423
|
+
for branch in Numeric.by_depth(subclasses).branches
|
424
|
+
p branch
|
425
|
+
end
|
426
|
+
puts
|
427
|
+
|
428
|
+
puts "Nodes with ancestors:"
|
429
|
+
for x, a in Numeric.by_depth(subclasses).with_ancestors
|
430
|
+
print "\t"*a.size, "node is #{x}, ancestors is #{a.inspect}.\n"
|
431
|
+
end
|
432
|
+
puts
|
433
|
+
|
434
|
+
# print the inheritance hierarchy of a given class
|
435
|
+
if ARGV[0]
|
436
|
+
root = Object.const_get(ARGV[0])
|
437
|
+
for x, a in root.by_depth(subclasses).with_ancestors
|
438
|
+
print "\t"*a.size, x, "\n"
|
439
|
+
end
|
440
|
+
end
|
441
|
+
|
442
|
+
end
|