rooted_tree 0.1.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.
- checksums.yaml +7 -0
- data/.gitignore +9 -0
- data/.travis.yml +5 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +99 -0
- data/Rakefile +10 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/examples/filesystem_tree.rb +38 -0
- data/lib/rooted_tree.rb +9 -0
- data/lib/rooted_tree/node.rb +413 -0
- data/lib/rooted_tree/tree.rb +20 -0
- data/lib/rooted_tree/version.rb +5 -0
- data/rooted_tree.gemspec +26 -0
- metadata +115 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: a23c9e6f541bfd82e32fa1417fefd88b8357e843
|
4
|
+
data.tar.gz: 05ac69ac576ba55e81c05d064d1adaa73380a8e6
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: dcffedd7ac50e2220bdd1dd7e6dbc9158bda7144eaa79c60e5a3c39a15375e1e7a7b3e6b77a7d316d48c5c4b8115444e732f6f6659a7b6a304770e12596fc452
|
7
|
+
data.tar.gz: e76035b943dabc64f8455d158f6d0074cbbb3605eb253646045e9538fba115c7321d961c7936e86e2d4bc48b4e6f51ef72d834aae2c332ffd82a22e7e181051f
|
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2016 Sebastian Lindberg
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,99 @@
|
|
1
|
+
# RootedTree
|
2
|
+
|
3
|
+
[](http://badge.fury.io/rb/rooted_tree)
|
4
|
+
[](https://travis-ci.org/seblindberg/ruby-rooted_tree)
|
5
|
+
[](https://coveralls.io/github/seblindberg/ruby-rooted_tree?branch=master)
|
6
|
+
|
7
|
+
A tree is a connected graph with no cycles. There, that is plenty of explanation. Please refer to https://en.wikipedia.org/wiki/Tree_structure for a more in depth description, but if you need one this library probably is not for you.
|
8
|
+
|
9
|
+
This gem technically implements a _rooted, ordered tree_, but that name is a mouthful. It is ment to be used as a building block when working with any tree shaped data. For a brief recap of the terminology please see below.
|
10
|
+
|
11
|
+
A A is the root.
|
12
|
+
┌────┼──┐ B, C and D are all children of A.
|
13
|
+
B C D E is a descendant of A.
|
14
|
+
┌─┼─┐ ┌┴┐ │ A is of degree 3 while C is of degree 2.
|
15
|
+
E F G H I J F is a leaf.
|
16
|
+
|
17
|
+
## Installation
|
18
|
+
|
19
|
+
Add this line to your application's Gemfile:
|
20
|
+
|
21
|
+
```ruby
|
22
|
+
gem 'rooted_tree'
|
23
|
+
```
|
24
|
+
|
25
|
+
And then execute:
|
26
|
+
|
27
|
+
$ bundle
|
28
|
+
|
29
|
+
Or install it yourself as:
|
30
|
+
|
31
|
+
$ gem install rooted_tree
|
32
|
+
|
33
|
+
## Usage
|
34
|
+
|
35
|
+
Please see the documentation for the complete API.
|
36
|
+
|
37
|
+
```ruby
|
38
|
+
# Create some nodes
|
39
|
+
root = RootedTree::Node.new
|
40
|
+
child_a = RootedTree::Node.new
|
41
|
+
child_b = RootedTree::Node.new
|
42
|
+
|
43
|
+
# Put the two children below the root
|
44
|
+
root << child_a << child_b
|
45
|
+
|
46
|
+
# Look at the result
|
47
|
+
p root # => RootedTree::Node:0x3fd5d54efda0
|
48
|
+
# ├─╴RootedTree::Node:0x3fd5d54c3ea8
|
49
|
+
# └─╴RootedTree::Node:0x3fd5d54ba894
|
50
|
+
```
|
51
|
+
|
52
|
+
The gem is primarily ment to be extended by other classes. The following example builds a tree of the files in the file system and displays it much like the command line tool `tree`.
|
53
|
+
|
54
|
+
```ruby
|
55
|
+
class FileSystemItem < RootedTree::Node
|
56
|
+
attr_reader :name
|
57
|
+
|
58
|
+
def initialize name
|
59
|
+
super()
|
60
|
+
@name = name
|
61
|
+
end
|
62
|
+
|
63
|
+
def display
|
64
|
+
inspect { |item| item.name }
|
65
|
+
end
|
66
|
+
|
67
|
+
def self.map_to_path path = '.', root: new(path)
|
68
|
+
# Iterate over all of the files in the directory
|
69
|
+
Dir[path + '/*'].each do |entry|
|
70
|
+
# Create a new FileSystemItem for the entry
|
71
|
+
item = new File.basename entry
|
72
|
+
root << item
|
73
|
+
# Continue to map the files and directories under
|
74
|
+
# entry, if it is a directory
|
75
|
+
map_to_path entry, root: item unless File.file? entry
|
76
|
+
end
|
77
|
+
|
78
|
+
root
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
puts FileSystemItem.map_to_path('.').display
|
83
|
+
```
|
84
|
+
|
85
|
+
## Development
|
86
|
+
|
87
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
88
|
+
|
89
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
90
|
+
|
91
|
+
## Contributing
|
92
|
+
|
93
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/rooted_tree.
|
94
|
+
|
95
|
+
|
96
|
+
## License
|
97
|
+
|
98
|
+
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
99
|
+
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "rooted_tree"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start
|
data/bin/setup
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'rooted_tree'
|
4
|
+
|
5
|
+
# FileSystemItem
|
6
|
+
#
|
7
|
+
# Maps the entries in the file system to `Node` objects via .map_to_path. The
|
8
|
+
# Node#inspect method is then exploited in #display to show the resulting tree
|
9
|
+
# structure.
|
10
|
+
|
11
|
+
class FileSystemItem < RootedTree::Node
|
12
|
+
attr_reader :name
|
13
|
+
|
14
|
+
def initialize name
|
15
|
+
super()
|
16
|
+
@name = name
|
17
|
+
end
|
18
|
+
|
19
|
+
def display
|
20
|
+
inspect { |item| item.name }
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.map_to_path path = '.', root: new(path)
|
24
|
+
# Iterate over all of the files in the directory
|
25
|
+
Dir[path + '/*'].each do |entry|
|
26
|
+
# Create a new FileSystemItem for the entry
|
27
|
+
item = new File.basename entry
|
28
|
+
root << item
|
29
|
+
# Continue to map the files and directories under
|
30
|
+
# entry, if it is a directory
|
31
|
+
map_to_path entry, root: item unless File.file? entry
|
32
|
+
end
|
33
|
+
|
34
|
+
root
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
puts FileSystemItem.map_to_path('.').display
|
data/lib/rooted_tree.rb
ADDED
@@ -0,0 +1,413 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Node
|
4
|
+
#
|
5
|
+
#
|
6
|
+
# The following is an example of a rooted tree of height 3.
|
7
|
+
#
|
8
|
+
# r - r, a, b, c, and d are internal vertices
|
9
|
+
# +--+---+ - vertices e, f, g, h, i, and j are leaves
|
10
|
+
# a b c - vertices g, h, and i are siblings
|
11
|
+
# +++ | +-+-+ - node a is an ancestor of j
|
12
|
+
# d e f g h i - j is a descendant of a
|
13
|
+
# |
|
14
|
+
# j
|
15
|
+
#
|
16
|
+
# The terminology is mostly referenced from
|
17
|
+
# http://www.cs.columbia.edu/~cs4203/files/GT-Lec4.pdf.
|
18
|
+
|
19
|
+
module RootedTree
|
20
|
+
class Node
|
21
|
+
attr_accessor :first_child, :last_child
|
22
|
+
attr_writer :next, :prev, :parent
|
23
|
+
protected :next=, :prev=, :parent=, :first_child=, :last_child=
|
24
|
+
|
25
|
+
def initialize
|
26
|
+
@parent = nil
|
27
|
+
@next = nil
|
28
|
+
@prev = nil
|
29
|
+
@first_child = nil
|
30
|
+
@last_child = nil
|
31
|
+
end
|
32
|
+
|
33
|
+
def initialize_copy(*)
|
34
|
+
duped_children = children.map { |v| v.dup.tap { |w| w.parent = self } }
|
35
|
+
duped_children.each_cons(2) { |a, b| a.next, b.prev = b, a }
|
36
|
+
|
37
|
+
@parent = nil
|
38
|
+
@first_child = duped_children.first
|
39
|
+
@last_child = duped_children.last
|
40
|
+
end
|
41
|
+
|
42
|
+
# Leaf?
|
43
|
+
#
|
44
|
+
# A node is a leaf if it has no children.
|
45
|
+
|
46
|
+
def leaf?
|
47
|
+
@first_child.nil?
|
48
|
+
end
|
49
|
+
|
50
|
+
# Internal?
|
51
|
+
#
|
52
|
+
# Returns true if the node is internal, which is equivalent to it having
|
53
|
+
# children.
|
54
|
+
|
55
|
+
def internal?
|
56
|
+
!leaf?
|
57
|
+
end
|
58
|
+
|
59
|
+
# Root?
|
60
|
+
#
|
61
|
+
# Returns true if node has no parent.
|
62
|
+
|
63
|
+
def root?
|
64
|
+
@parent.nil?
|
65
|
+
end
|
66
|
+
|
67
|
+
# Root
|
68
|
+
#
|
69
|
+
# Returns the root of the tree.
|
70
|
+
|
71
|
+
def root
|
72
|
+
return self if root?
|
73
|
+
|
74
|
+
node = self
|
75
|
+
loop { node = node.parent }
|
76
|
+
node
|
77
|
+
end
|
78
|
+
|
79
|
+
# First?
|
80
|
+
#
|
81
|
+
# Returns true if this node is the first of its siblings.
|
82
|
+
|
83
|
+
def first?
|
84
|
+
@prev.nil?
|
85
|
+
end
|
86
|
+
|
87
|
+
# Last?
|
88
|
+
#
|
89
|
+
# Returns true if this node is the last of its siblings.
|
90
|
+
|
91
|
+
def last?
|
92
|
+
@next.nil?
|
93
|
+
end
|
94
|
+
|
95
|
+
# Depth
|
96
|
+
#
|
97
|
+
# Returns the depth of the node within the tree
|
98
|
+
|
99
|
+
def depth
|
100
|
+
ancestors.count
|
101
|
+
end
|
102
|
+
|
103
|
+
alias level depth
|
104
|
+
|
105
|
+
# Degree
|
106
|
+
#
|
107
|
+
# Returns the number of children of the node.
|
108
|
+
|
109
|
+
def degree
|
110
|
+
children.count
|
111
|
+
end
|
112
|
+
|
113
|
+
alias arity degree
|
114
|
+
|
115
|
+
# Size
|
116
|
+
#
|
117
|
+
# Calculate the size in vertecies of the subtree.
|
118
|
+
|
119
|
+
def size
|
120
|
+
children.reduce(1) { |a, e| a + e.size }
|
121
|
+
end
|
122
|
+
|
123
|
+
# Next
|
124
|
+
#
|
125
|
+
# Access the next sibling. Raises a StopIteration if this node is the last
|
126
|
+
# one.
|
127
|
+
|
128
|
+
def next
|
129
|
+
raise StopIteration if last?
|
130
|
+
@next
|
131
|
+
end
|
132
|
+
|
133
|
+
# Prev(ious)
|
134
|
+
#
|
135
|
+
# Access the previous sibling. Raises a StopIteration if this node is the
|
136
|
+
# first one.
|
137
|
+
|
138
|
+
def prev
|
139
|
+
raise StopIteration if first?
|
140
|
+
@prev
|
141
|
+
end
|
142
|
+
|
143
|
+
alias previous prev
|
144
|
+
|
145
|
+
# Parent
|
146
|
+
#
|
147
|
+
# Access the parent node. Raises a StopIteration if this node is the
|
148
|
+
# root.
|
149
|
+
|
150
|
+
def parent
|
151
|
+
raise StopIteration if root?
|
152
|
+
@parent
|
153
|
+
end
|
154
|
+
|
155
|
+
# Append Sibling
|
156
|
+
#
|
157
|
+
# Insert a child between this node and the one after it.
|
158
|
+
|
159
|
+
def append_sibling(node)
|
160
|
+
raise StructureException, 'Root node can not have siblings' if root?
|
161
|
+
|
162
|
+
node.next = @next
|
163
|
+
node.prev = self
|
164
|
+
node.parent = @parent
|
165
|
+
if @next
|
166
|
+
@next.prev = node
|
167
|
+
else
|
168
|
+
@parent.last_child = node
|
169
|
+
end
|
170
|
+
@next = node
|
171
|
+
end
|
172
|
+
|
173
|
+
# Prepend Sibling
|
174
|
+
#
|
175
|
+
# Insert a child between this node and the one before it.
|
176
|
+
|
177
|
+
def prepend_sibling(node)
|
178
|
+
raise StructureException, 'Root node can not have siblings' if root?
|
179
|
+
|
180
|
+
node.next = self
|
181
|
+
node.prev = @prev
|
182
|
+
node.parent = @parent
|
183
|
+
if @prev
|
184
|
+
@prev.next = node
|
185
|
+
else
|
186
|
+
@parent.first_child = node
|
187
|
+
end
|
188
|
+
@prev = node
|
189
|
+
end
|
190
|
+
|
191
|
+
# Append Child
|
192
|
+
#
|
193
|
+
# Insert a child after the last one.
|
194
|
+
|
195
|
+
def append_child(child)
|
196
|
+
if leaf?
|
197
|
+
@first_child = @last_child = child
|
198
|
+
child.next = child.prev = nil
|
199
|
+
child.parent = self
|
200
|
+
else
|
201
|
+
@last_child.append_sibling child
|
202
|
+
end
|
203
|
+
self
|
204
|
+
end
|
205
|
+
|
206
|
+
# Prepend Child
|
207
|
+
#
|
208
|
+
# Insert a child before the first one.
|
209
|
+
|
210
|
+
def prepend_child(child)
|
211
|
+
if leaf?
|
212
|
+
@first_child = @last_child = child
|
213
|
+
child.next = child.prev = nil
|
214
|
+
child.parent = self
|
215
|
+
else
|
216
|
+
@first_child.prepend_sibling child
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
alias << append_child
|
221
|
+
|
222
|
+
# Extract
|
223
|
+
#
|
224
|
+
# Extracts the node and its subtree from the larger structure.
|
225
|
+
|
226
|
+
def extract
|
227
|
+
return self if root?
|
228
|
+
|
229
|
+
if last?
|
230
|
+
parent.last_child = @prev
|
231
|
+
else
|
232
|
+
@next.prev = @prev
|
233
|
+
end
|
234
|
+
|
235
|
+
if first?
|
236
|
+
parent.first_child = @next
|
237
|
+
else
|
238
|
+
@prev.next = @next
|
239
|
+
end
|
240
|
+
|
241
|
+
@prev = @next = @parent = nil
|
242
|
+
self
|
243
|
+
end
|
244
|
+
|
245
|
+
# Delete
|
246
|
+
#
|
247
|
+
# Removes the node from the tree.
|
248
|
+
|
249
|
+
def delete
|
250
|
+
extract.children.map do |child|
|
251
|
+
child.parent = nil
|
252
|
+
child
|
253
|
+
end
|
254
|
+
end
|
255
|
+
|
256
|
+
# Ancestors
|
257
|
+
#
|
258
|
+
# Returns an enumerator that will iterate over the parents of this node
|
259
|
+
# until the root is reached.
|
260
|
+
#
|
261
|
+
# If a block is given it will be yielded to.
|
262
|
+
|
263
|
+
def ancestors
|
264
|
+
return to_enum(__callee__) unless block_given?
|
265
|
+
node = self
|
266
|
+
loop do
|
267
|
+
node = node.parent
|
268
|
+
yield node
|
269
|
+
end
|
270
|
+
end
|
271
|
+
|
272
|
+
# Children
|
273
|
+
#
|
274
|
+
# Returns an enumerator that will iterate over each of the node children.
|
275
|
+
# The default order is left-to-right, but by passing rtl: true the order can
|
276
|
+
# be reversed.
|
277
|
+
#
|
278
|
+
# If a block is given it will be yielded to.
|
279
|
+
|
280
|
+
def children(rtl: false)
|
281
|
+
return to_enum(__callee__, rtl: rtl) unless block_given?
|
282
|
+
return if leaf?
|
283
|
+
|
284
|
+
child, advance = if rtl
|
285
|
+
[@last_child, :prev]
|
286
|
+
else
|
287
|
+
[@first_child, :next]
|
288
|
+
end
|
289
|
+
|
290
|
+
loop do
|
291
|
+
yield child
|
292
|
+
child = child.send advance
|
293
|
+
end
|
294
|
+
end
|
295
|
+
|
296
|
+
# Each
|
297
|
+
#
|
298
|
+
#
|
299
|
+
|
300
|
+
def each(&block)
|
301
|
+
return to_enum(__callee__) unless block_given?
|
302
|
+
yield self
|
303
|
+
children { |v| v.each(&block) }
|
304
|
+
end
|
305
|
+
|
306
|
+
# Leafs
|
307
|
+
#
|
308
|
+
# Iterates over each of the leafs.
|
309
|
+
|
310
|
+
def leafs(rtl: false, &block)
|
311
|
+
return to_enum(__callee__, rtl: rtl) unless block_given?
|
312
|
+
return yield self if leaf?
|
313
|
+
children(rtl: rtl) { |v| v.leafs(rtl: rtl, &block) }
|
314
|
+
end
|
315
|
+
|
316
|
+
# Edges
|
317
|
+
#
|
318
|
+
# Iterates over each of the edges.
|
319
|
+
|
320
|
+
def edges(&block)
|
321
|
+
return to_enum(__callee__) unless block_given?
|
322
|
+
|
323
|
+
children do |v|
|
324
|
+
yield self, v
|
325
|
+
v.edges(&block)
|
326
|
+
end
|
327
|
+
end
|
328
|
+
|
329
|
+
# Add
|
330
|
+
#
|
331
|
+
# Add two roots together to create a larger tree. A new common root will be
|
332
|
+
# created and returned.
|
333
|
+
|
334
|
+
def +(other)
|
335
|
+
unless root? && other.root?
|
336
|
+
raise StructureException, 'Only roots can be added'
|
337
|
+
end
|
338
|
+
|
339
|
+
root = self.class.new
|
340
|
+
root << self << other
|
341
|
+
end
|
342
|
+
|
343
|
+
# Equality
|
344
|
+
#
|
345
|
+
# Returns true if the two vertecies form identical subtrees
|
346
|
+
|
347
|
+
def ==(other)
|
348
|
+
return false unless other.is_a? self.class
|
349
|
+
return other.leaf? if leaf?
|
350
|
+
|
351
|
+
children.to_a == other.children.to_a
|
352
|
+
end
|
353
|
+
|
354
|
+
# Tree
|
355
|
+
#
|
356
|
+
# Wraps the entire tree in a Tree object If this node is a child the root
|
357
|
+
# will be found and passed to Tree.new.
|
358
|
+
|
359
|
+
def tree
|
360
|
+
Tree.new root
|
361
|
+
end
|
362
|
+
|
363
|
+
# Subtree!
|
364
|
+
#
|
365
|
+
# Extracts this node from the larger tree and wraps it in a Tree object.
|
366
|
+
|
367
|
+
def subtree!
|
368
|
+
Tree.new extract
|
369
|
+
end
|
370
|
+
|
371
|
+
# Subtree
|
372
|
+
#
|
373
|
+
# Duplicates this node and its descendants and wraps them in a Tree object.
|
374
|
+
|
375
|
+
def subtree
|
376
|
+
Tree.new dup
|
377
|
+
end
|
378
|
+
|
379
|
+
# Inspect
|
380
|
+
#
|
381
|
+
# Visalizes the tree structure in a style very similar to the cli tool tree.
|
382
|
+
# An example of the output can be seen below. Note that the output string
|
383
|
+
# contains unicode characters.
|
384
|
+
#
|
385
|
+
# Node:0x3ffd64c22abc
|
386
|
+
# |--Node:0x3ffd64c1fd30
|
387
|
+
# | |--Node:0x3ffd64c1f86c
|
388
|
+
# | +--Node:0x3ffd64c1f63c
|
389
|
+
# +--Entety:0x3ffd64c1f40c
|
390
|
+
#
|
391
|
+
# By passing `as_array: true` the method will instead return an array
|
392
|
+
# containing each of the output lines. The method also accepts a block
|
393
|
+
# which, if given, will be yielded to once for every node, and the output
|
394
|
+
# will be used as node labels instead of the default identifier.
|
395
|
+
|
396
|
+
def inspect(as_array: false, &block)
|
397
|
+
unless block_given?
|
398
|
+
block = proc { |v| format '%s:0x%0x', v.class.name, v.object_id }
|
399
|
+
end
|
400
|
+
|
401
|
+
res = [block.call(self)]
|
402
|
+
|
403
|
+
children do |child|
|
404
|
+
lines = child.inspect(as_array: true, &block).each
|
405
|
+
res << ((child.last? ? '└─╴' : '├─╴') + lines.next)
|
406
|
+
prep = child.last? ? ' ' : '│ '
|
407
|
+
loop { res << (prep + lines.next) }
|
408
|
+
end
|
409
|
+
|
410
|
+
as_array ? res : res.join("\n")
|
411
|
+
end
|
412
|
+
end
|
413
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module RootedTree
|
2
|
+
class Tree
|
3
|
+
attr_reader :root
|
4
|
+
|
5
|
+
def initialize(node = Node.new)
|
6
|
+
@root = node.root
|
7
|
+
end
|
8
|
+
|
9
|
+
# Degree
|
10
|
+
#
|
11
|
+
# Returns the maximum degree (number of children) in the tree.
|
12
|
+
|
13
|
+
def degree
|
14
|
+
max_degree_node = root.each.max_by do |node|
|
15
|
+
node.degree
|
16
|
+
end
|
17
|
+
max_degree_node.degree
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
data/rooted_tree.gemspec
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'rooted_tree/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "rooted_tree"
|
8
|
+
spec.version = RootedTree::VERSION
|
9
|
+
spec.authors = ["Sebastian Lindberg"]
|
10
|
+
spec.email = ["seb.lindberg@gmail.com"]
|
11
|
+
|
12
|
+
spec.summary = %q{A basic implementation of a tree data structure.}
|
13
|
+
spec.description = %q{This gem implements a rooted, ordered tree, with a focus on easy iteration over nodes and access to basic tree prperties.}
|
14
|
+
spec.homepage = "https://github.com/seblindberg/ruby-rooted_tree"
|
15
|
+
spec.license = "MIT"
|
16
|
+
|
17
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
18
|
+
spec.bindir = "exe"
|
19
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
20
|
+
spec.require_paths = ["lib"]
|
21
|
+
|
22
|
+
spec.add_development_dependency "bundler", "~> 1.12"
|
23
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
24
|
+
spec.add_development_dependency "minitest", "~> 5.0"
|
25
|
+
spec.add_development_dependency "coveralls", "~> 0.8"
|
26
|
+
end
|
metadata
ADDED
@@ -0,0 +1,115 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rooted_tree
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Sebastian Lindberg
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-07-23 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.12'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.12'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: minitest
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '5.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '5.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: coveralls
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0.8'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0.8'
|
69
|
+
description: This gem implements a rooted, ordered tree, with a focus on easy iteration
|
70
|
+
over nodes and access to basic tree prperties.
|
71
|
+
email:
|
72
|
+
- seb.lindberg@gmail.com
|
73
|
+
executables: []
|
74
|
+
extensions: []
|
75
|
+
extra_rdoc_files: []
|
76
|
+
files:
|
77
|
+
- ".gitignore"
|
78
|
+
- ".travis.yml"
|
79
|
+
- Gemfile
|
80
|
+
- LICENSE.txt
|
81
|
+
- README.md
|
82
|
+
- Rakefile
|
83
|
+
- bin/console
|
84
|
+
- bin/setup
|
85
|
+
- examples/filesystem_tree.rb
|
86
|
+
- lib/rooted_tree.rb
|
87
|
+
- lib/rooted_tree/node.rb
|
88
|
+
- lib/rooted_tree/tree.rb
|
89
|
+
- lib/rooted_tree/version.rb
|
90
|
+
- rooted_tree.gemspec
|
91
|
+
homepage: https://github.com/seblindberg/ruby-rooted_tree
|
92
|
+
licenses:
|
93
|
+
- MIT
|
94
|
+
metadata: {}
|
95
|
+
post_install_message:
|
96
|
+
rdoc_options: []
|
97
|
+
require_paths:
|
98
|
+
- lib
|
99
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
105
|
+
requirements:
|
106
|
+
- - ">="
|
107
|
+
- !ruby/object:Gem::Version
|
108
|
+
version: '0'
|
109
|
+
requirements: []
|
110
|
+
rubyforge_project:
|
111
|
+
rubygems_version: 2.5.1
|
112
|
+
signing_key:
|
113
|
+
specification_version: 4
|
114
|
+
summary: A basic implementation of a tree data structure.
|
115
|
+
test_files: []
|