splay_tree 0.1.0 → 0.2.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 +4 -4
- data/.travis.yml +1 -0
- data/CHANGELOG.md +5 -0
- data/Gemfile +1 -0
- data/README.md +80 -9
- data/benchmarks/speed.rb +40 -0
- data/lib/splay_tree/node.rb +27 -4
- data/lib/splay_tree/version.rb +1 -1
- data/lib/splay_tree.rb +113 -3
- data/spec/unit/default_spec.rb +28 -0
- data/spec/unit/each_spec.rb +55 -0
- data/spec/unit/to_hash_spec.rb +12 -0
- metadata +10 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c66b504221015b64dc0e1ea92bfe583eb279a9e3
|
4
|
+
data.tar.gz: d7a06439164029d713e38135ffff5786435675aa
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 074fe40542d0b6665e01a8dc5a31b2efe7402676c5d7e78cb95a30da616e146cd83801d9f5a401cc76bf90cf7a29c2f1adf36f9c6b1c33a433aff3c720189ef7
|
7
|
+
data.tar.gz: 4f0d1375ef8aed9e1d30fc632a3fe1e44eb3f4765f4411314de5fd6eaf8e6df7a9fc896494ce16b3152a4d35f9901a64690c539189791b52c3dd57238797b64f
|
data/.travis.yml
CHANGED
data/CHANGELOG.md
ADDED
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -3,11 +3,13 @@
|
|
3
3
|
[][travis]
|
4
4
|
[][codeclimate]
|
5
5
|
[][coverage]
|
6
|
+
[][inchpages]
|
6
7
|
|
7
8
|
[gem]: http://badge.fury.io/rb/splay_tree
|
8
9
|
[travis]: http://travis-ci.org/peter-murach/splay_tree
|
9
10
|
[codeclimate]: https://codeclimate.com/github/peter-murach/splay_tree
|
10
11
|
[coverage]: https://coveralls.io/r/peter-murach/splay_tree
|
12
|
+
[inchpages]: http://inch-ci.org/github/peter-murach/splay_tree
|
11
13
|
|
12
14
|
> Self balancing binary tree that keeps lookup operations fast by optimizing frequently accessed keys. Useful for implementing caches and garbage collection algorithms.
|
13
15
|
|
@@ -32,6 +34,16 @@ Or install it yourself as:
|
|
32
34
|
|
33
35
|
$ gem install splay_tree
|
34
36
|
|
37
|
+
## Contents
|
38
|
+
|
39
|
+
* [1. Usage](#1-usage)
|
40
|
+
* [1.1 insert](#11-insert)
|
41
|
+
* [1.2 fetch](#12-fetch)
|
42
|
+
* [1.3 default](#13-default)
|
43
|
+
* [1.4 delete](#14-delete)
|
44
|
+
* [1.5 empty?](#15-empty)
|
45
|
+
* [1.6 each](#16-each)
|
46
|
+
|
35
47
|
## 1. Usage
|
36
48
|
|
37
49
|
**SplayTree** operations are similar to that of `Hash`:
|
@@ -46,28 +58,66 @@ tree.size # => 1
|
|
46
58
|
|
47
59
|
### 1.1 insert
|
48
60
|
|
49
|
-
In order to
|
61
|
+
In order to associate the value with the given key do:
|
50
62
|
|
51
63
|
```ruby
|
52
|
-
tree
|
53
|
-
tree[
|
64
|
+
tree = SplayTree.new
|
65
|
+
tree["a"] = 1
|
66
|
+
tree["b"] = 2
|
54
67
|
```
|
55
68
|
|
69
|
+
Note: Inserted key will be subjected to splaying, which means the tree will be rearranged to help with quicker access on subsequent calls.
|
70
|
+
|
56
71
|
### 1.2 fetch
|
57
72
|
|
58
|
-
To
|
73
|
+
To retrieve a value from the tree corresponding to the key do:
|
74
|
+
|
75
|
+
```ruby
|
76
|
+
tree = SplayTree.new
|
77
|
+
tree["a"] # => nil
|
78
|
+
|
79
|
+
tree["a"] = 1
|
80
|
+
tree["a"] # => 1
|
81
|
+
```
|
82
|
+
|
83
|
+
Note: Frequently accessed keys will move nearer to the root where they can be accessed more quickly.
|
84
|
+
|
85
|
+
### 1.3 default
|
86
|
+
|
87
|
+
**SplayTree** allows you to set default value if key does not exist. This can be done during initialization or using `default` method:
|
88
|
+
|
89
|
+
```ruby
|
90
|
+
tree = SplayTree.new
|
91
|
+
tree.default # => UndefinedValue
|
92
|
+
|
93
|
+
tree = SplayTree.new("foo")
|
94
|
+
tree.default # => "foo"
|
95
|
+
tree["a"] # => "foo"
|
96
|
+
```
|
97
|
+
|
98
|
+
You can also use block to set default value:
|
59
99
|
|
60
100
|
```ruby
|
61
|
-
tree
|
101
|
+
tree = SplayTree.new
|
102
|
+
tree.default_proc # => nil
|
103
|
+
|
104
|
+
tree = SplayTree.new { "foo" }
|
105
|
+
tree.default_proc # => "foo"
|
106
|
+
tree["a"] # => "foo"
|
62
107
|
```
|
63
108
|
|
64
|
-
### 1.
|
109
|
+
### 1.4 delete
|
110
|
+
|
111
|
+
In order to remove an entry from the splay tree use `delete`. If the value is not found the default value is returned and `nil` otherwise.
|
65
112
|
|
66
113
|
```ruby
|
114
|
+
tree = SplayTree.new
|
115
|
+
tree['a'] = 1
|
67
116
|
tree.delete('a') # => 1
|
117
|
+
tree.delete('z') # => nil
|
68
118
|
```
|
69
119
|
|
70
|
-
### 1.
|
120
|
+
### 1.5 empty?
|
71
121
|
|
72
122
|
To check if `tree` contains any elements call `empty?` like so:
|
73
123
|
|
@@ -75,10 +125,31 @@ To check if `tree` contains any elements call `empty?` like so:
|
|
75
125
|
tree = SplayTree.new
|
76
126
|
tree.empty? # => true
|
77
127
|
|
78
|
-
tree[
|
128
|
+
tree["a"] = 1
|
79
129
|
tree.empty? # => false
|
80
130
|
```
|
81
131
|
|
132
|
+
### 1.6 each
|
133
|
+
|
134
|
+
In order to iterate over all tree nodes use `each` method like so:
|
135
|
+
|
136
|
+
```ruby
|
137
|
+
tree = SplayTree.new
|
138
|
+
tree['a'] = 1
|
139
|
+
tree['b'] = 2
|
140
|
+
|
141
|
+
tree.each { |key, value| puts "#{key}: #{value}" }
|
142
|
+
```
|
143
|
+
|
144
|
+
In addition you can use `each_key`, `each_value` to enumerate only keys and values respectively.
|
145
|
+
|
146
|
+
```ruby
|
147
|
+
tree.each_key { |key| ... }
|
148
|
+
tree.each_value { |value| ... }
|
149
|
+
```
|
150
|
+
|
151
|
+
If no block is provided, an enumerator is returned instead.
|
152
|
+
|
82
153
|
## Contributing
|
83
154
|
|
84
155
|
1. Fork it ( https://github.com/peter-murach/splay_tree/fork )
|
@@ -89,4 +160,4 @@ tree.empty? # => false
|
|
89
160
|
|
90
161
|
## Copyright
|
91
162
|
|
92
|
-
Copyright (c) 2014 Piotr Murach. See LICENSE for further details.
|
163
|
+
Copyright (c) 2014-2015 Piotr Murach. See LICENSE for further details.
|
data/benchmarks/speed.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
require 'benchmark/ips'
|
4
|
+
require 'splay_tree'
|
5
|
+
require 'securerandom'
|
6
|
+
|
7
|
+
def generate_key(size)
|
8
|
+
SecureRandom.hex(size)
|
9
|
+
end
|
10
|
+
|
11
|
+
def insert_key(tree)
|
12
|
+
key = generate_key(10)
|
13
|
+
tree[key] = 1
|
14
|
+
key
|
15
|
+
end
|
16
|
+
|
17
|
+
def run(bench, object)
|
18
|
+
name = object.class.name
|
19
|
+
|
20
|
+
key = nil
|
21
|
+
|
22
|
+
bench.report("#{name} insert") do
|
23
|
+
key = insert_key(object)
|
24
|
+
end
|
25
|
+
|
26
|
+
bench.report("#{name} find") do
|
27
|
+
object[key]
|
28
|
+
end
|
29
|
+
|
30
|
+
bench.report("#{name} delete") do
|
31
|
+
object.delete(key)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
Benchmark.ips do |bench|
|
36
|
+
bench.config(time: 5, warmpu: 2)
|
37
|
+
|
38
|
+
run(bench, SplayTree.new)
|
39
|
+
run(bench, Hash.new)
|
40
|
+
end
|
data/lib/splay_tree/node.rb
CHANGED
@@ -43,6 +43,29 @@ class SplayTree
|
|
43
43
|
@key <=> other.key
|
44
44
|
end
|
45
45
|
|
46
|
+
# Iterate over subtree nodes
|
47
|
+
#
|
48
|
+
# @api private
|
49
|
+
def each(&block)
|
50
|
+
@left.each(&block)
|
51
|
+
yield [@key, @value]
|
52
|
+
@right.each(&block)
|
53
|
+
end
|
54
|
+
|
55
|
+
# Iterate over subtree nodes
|
56
|
+
#
|
57
|
+
# @api private
|
58
|
+
def each_key(&block)
|
59
|
+
each { |k, v| yield k }
|
60
|
+
end
|
61
|
+
|
62
|
+
# Iterate over subtree nodes
|
63
|
+
#
|
64
|
+
# @api private
|
65
|
+
def each_value(&block)
|
66
|
+
each { |k, v| yield v }
|
67
|
+
end
|
68
|
+
|
46
69
|
# Dump the subtree structure starting from this node
|
47
70
|
#
|
48
71
|
# @return [String]
|
@@ -121,11 +144,11 @@ class SplayTree
|
|
121
144
|
0
|
122
145
|
end
|
123
146
|
|
124
|
-
def to_s
|
125
|
-
end
|
147
|
+
def to_s; end
|
126
148
|
|
127
|
-
def dump
|
128
|
-
|
149
|
+
def dump; end
|
150
|
+
|
151
|
+
def each(&block); end
|
129
152
|
|
130
153
|
def insert(key, value)
|
131
154
|
Node.new(key, value)
|
data/lib/splay_tree/version.rb
CHANGED
data/lib/splay_tree.rb
CHANGED
@@ -4,10 +4,26 @@ require 'splay_tree/node'
|
|
4
4
|
require 'splay_tree/version'
|
5
5
|
|
6
6
|
class SplayTree
|
7
|
+
include Enumerable
|
8
|
+
|
7
9
|
UndefinedValue = Module.new
|
8
10
|
|
11
|
+
# The default value for non existent key
|
12
|
+
#
|
13
|
+
# @api public
|
9
14
|
attr_accessor :default
|
10
15
|
|
16
|
+
# The default block for non existent key
|
17
|
+
#
|
18
|
+
# @api public
|
19
|
+
attr_accessor :default_proc
|
20
|
+
|
21
|
+
# Create a SplayTree
|
22
|
+
#
|
23
|
+
# @param [Object] default
|
24
|
+
# the default value for missing key
|
25
|
+
#
|
26
|
+
# @api public
|
11
27
|
def initialize(default = UndefinedValue, &block)
|
12
28
|
if !UndefinedValue.equal?(default) && block
|
13
29
|
fail ArgumentError,
|
@@ -16,7 +32,7 @@ class SplayTree
|
|
16
32
|
@root = Node::EMPTY
|
17
33
|
@subtree = Node.new(nil, nil)
|
18
34
|
@default = default
|
19
|
-
@
|
35
|
+
@default_proc = block
|
20
36
|
end
|
21
37
|
|
22
38
|
# @api public
|
@@ -30,6 +46,91 @@ class SplayTree
|
|
30
46
|
end
|
31
47
|
alias_method :length, :size
|
32
48
|
|
49
|
+
# Iterate over each key & value pair in the tree
|
50
|
+
#
|
51
|
+
# @example
|
52
|
+
# tree = SplayTree.new
|
53
|
+
# tree.each { |key, val| ... }
|
54
|
+
#
|
55
|
+
# @yield [key, value]
|
56
|
+
#
|
57
|
+
# @yieldparam [Object] key
|
58
|
+
# @yieldparam [Object] value
|
59
|
+
#
|
60
|
+
# @return [self]
|
61
|
+
#
|
62
|
+
# @api public
|
63
|
+
def each(&block)
|
64
|
+
if block_given?
|
65
|
+
@root.each(&block)
|
66
|
+
self
|
67
|
+
else
|
68
|
+
@root.to_enum
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
# Iterate over each key in the tree
|
73
|
+
#
|
74
|
+
# @example
|
75
|
+
# tree = SplayTree.new
|
76
|
+
# tree.each_key { |key| ... }
|
77
|
+
#
|
78
|
+
# @yield [key]
|
79
|
+
#
|
80
|
+
# @yieldparam [Object] key
|
81
|
+
#
|
82
|
+
# @return [self]
|
83
|
+
#
|
84
|
+
# @api public
|
85
|
+
def each_key(&block)
|
86
|
+
if block_given?
|
87
|
+
@root.each_key(&block)
|
88
|
+
self
|
89
|
+
else
|
90
|
+
@root.to_enum(:each_key)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
# Iterate over each value in the tree
|
95
|
+
#
|
96
|
+
# @example
|
97
|
+
# tree = SplayTree.new
|
98
|
+
# tree.each_value { |val| ... }
|
99
|
+
#
|
100
|
+
# @yield [value]
|
101
|
+
#
|
102
|
+
# @yieldparam [Object] value
|
103
|
+
#
|
104
|
+
# @return [self]
|
105
|
+
#
|
106
|
+
# @api public
|
107
|
+
def each_value(&block)
|
108
|
+
if block_given?
|
109
|
+
@root.each_value(&block)
|
110
|
+
self
|
111
|
+
else
|
112
|
+
@root.to_enum(:each_value)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
# Return a new array of all the keys in the tree
|
117
|
+
#
|
118
|
+
# @return [Array[Object]]
|
119
|
+
#
|
120
|
+
# @api public
|
121
|
+
def keys
|
122
|
+
each_key.to_a
|
123
|
+
end
|
124
|
+
|
125
|
+
# Return a new array of all the values in the tree
|
126
|
+
#
|
127
|
+
# @return [Array[Object]]
|
128
|
+
#
|
129
|
+
# @api public
|
130
|
+
def values
|
131
|
+
each_value.to_a
|
132
|
+
end
|
133
|
+
|
33
134
|
# Insert a node into a tree with the given key and value
|
34
135
|
# provided that the tree does not already contain the key.
|
35
136
|
# The node becomes the root of the tree.
|
@@ -127,14 +228,23 @@ class SplayTree
|
|
127
228
|
@root.dump || ''
|
128
229
|
end
|
129
230
|
|
231
|
+
# Export tree as hash
|
232
|
+
#
|
233
|
+
# @return [Hash]
|
234
|
+
#
|
235
|
+
# @api public
|
236
|
+
def to_hash
|
237
|
+
reduce({}) { |acc, (k, v)| acc[k] = v; acc }
|
238
|
+
end
|
239
|
+
|
130
240
|
private
|
131
241
|
|
132
242
|
# @api private
|
133
243
|
def default_value
|
134
244
|
if @default != UndefinedValue
|
135
245
|
@default
|
136
|
-
elsif @
|
137
|
-
@
|
246
|
+
elsif @default_proc
|
247
|
+
@default_proc.call
|
138
248
|
end
|
139
249
|
end
|
140
250
|
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
RSpec.describe SplayTree, '.default' do
|
6
|
+
it "sets the default value on initialize" do
|
7
|
+
tree = SplayTree.new('foo')
|
8
|
+
expect(tree.default).to eq('foo')
|
9
|
+
expect(tree['a']).to eq('foo')
|
10
|
+
expect(tree.default_proc).to eq(nil)
|
11
|
+
end
|
12
|
+
|
13
|
+
it "sets the default value on assigment" do
|
14
|
+
tree = SplayTree.new
|
15
|
+
tree.default = 'foo'
|
16
|
+
expect(tree.default).to eq('foo')
|
17
|
+
expect(tree['a']).to eq('foo')
|
18
|
+
expect(tree.default_proc).to eq(nil)
|
19
|
+
end
|
20
|
+
|
21
|
+
it "sets the default_proc to be executed on each key lookup" do
|
22
|
+
block = -> { 'foo' }
|
23
|
+
tree = SplayTree.new(&block)
|
24
|
+
expect(tree.default).to eq(SplayTree::UndefinedValue)
|
25
|
+
expect(tree.default_proc).to eq(block)
|
26
|
+
expect(tree['a']).to eq('foo')
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
RSpec.describe SplayTree, '.each' do
|
6
|
+
subject(:tree) { described_class.new }
|
7
|
+
|
8
|
+
before {
|
9
|
+
tree['a'] = 1
|
10
|
+
tree['b'] = 2
|
11
|
+
tree['c'] = 3
|
12
|
+
}
|
13
|
+
|
14
|
+
it "enumartes all pairs" do
|
15
|
+
yielded = []
|
16
|
+
expect {
|
17
|
+
tree.each { |k, v| yielded << [k, v] }
|
18
|
+
}.to change { yielded }.from([]).to([['a', 1], ['b', 2], ['c', 3]])
|
19
|
+
expect(tree.to_a).to eq(yielded)
|
20
|
+
end
|
21
|
+
|
22
|
+
it "returns enumerator for all pairs without block" do
|
23
|
+
expect(tree.each.to_a).to eq([['a', 1], ['b', 2], ['c', 3]])
|
24
|
+
end
|
25
|
+
|
26
|
+
it "enumerates all keys" do
|
27
|
+
yielded = []
|
28
|
+
expect {
|
29
|
+
tree.each_key { |k| yielded << k }
|
30
|
+
}.to change { yielded }.from([]).to(['a', 'b', 'c'])
|
31
|
+
end
|
32
|
+
|
33
|
+
it "returns enumerator for all keys without block" do
|
34
|
+
expect(tree.each_key.to_a).to eq(['a', 'b', 'c'])
|
35
|
+
end
|
36
|
+
|
37
|
+
it "enumerates all values" do
|
38
|
+
yielded = []
|
39
|
+
expect {
|
40
|
+
tree.each_value { |v| yielded << v }
|
41
|
+
}.to change { yielded }.from([]).to([1, 2, 3])
|
42
|
+
end
|
43
|
+
|
44
|
+
it "returns enumerator for all values without block" do
|
45
|
+
expect(tree.each_value.to_a).to eq([1, 2, 3])
|
46
|
+
end
|
47
|
+
|
48
|
+
it "populates all keys" do
|
49
|
+
expect(tree.keys).to eq(['a', 'b', 'c'])
|
50
|
+
end
|
51
|
+
|
52
|
+
it "populates all values" do
|
53
|
+
expect(tree.values).to eq([1, 2, 3])
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
RSpec.describe SplayTree, '.to_hash' do
|
6
|
+
it "exports tree as hash" do
|
7
|
+
tree = SplayTree.new
|
8
|
+
hash = {'a' => 1, 'ab' => 2, 'abc' => 3, 'abd' => 4, 'ac' => 5, 'b' => 6}
|
9
|
+
hash.each { |k, v| tree[k] = v }
|
10
|
+
expect(tree.to_hash).to eq(hash)
|
11
|
+
end
|
12
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: splay_tree
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Piotr Murach
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2015-01-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -37,18 +37,23 @@ files:
|
|
37
37
|
- .rspec
|
38
38
|
- .ruby-version
|
39
39
|
- .travis.yml
|
40
|
+
- CHANGELOG.md
|
40
41
|
- Gemfile
|
41
42
|
- LICENSE.txt
|
42
43
|
- README.md
|
43
44
|
- Rakefile
|
45
|
+
- benchmarks/speed.rb
|
44
46
|
- lib/splay_tree.rb
|
45
47
|
- lib/splay_tree/node.rb
|
46
48
|
- lib/splay_tree/version.rb
|
47
49
|
- spec/spec_helper.rb
|
50
|
+
- spec/unit/default_spec.rb
|
48
51
|
- spec/unit/delete_spec.rb
|
52
|
+
- spec/unit/each_spec.rb
|
49
53
|
- spec/unit/fetch_spec.rb
|
50
54
|
- spec/unit/insert_spec.rb
|
51
55
|
- spec/unit/new_spec.rb
|
56
|
+
- spec/unit/to_hash_spec.rb
|
52
57
|
- splay_tree.gemspec
|
53
58
|
- tasks/console.rake
|
54
59
|
- tasks/coverage.rake
|
@@ -79,8 +84,11 @@ specification_version: 4
|
|
79
84
|
summary: A self-balancing binary tree with amortized access.
|
80
85
|
test_files:
|
81
86
|
- spec/spec_helper.rb
|
87
|
+
- spec/unit/default_spec.rb
|
82
88
|
- spec/unit/delete_spec.rb
|
89
|
+
- spec/unit/each_spec.rb
|
83
90
|
- spec/unit/fetch_spec.rb
|
84
91
|
- spec/unit/insert_spec.rb
|
85
92
|
- spec/unit/new_spec.rb
|
93
|
+
- spec/unit/to_hash_spec.rb
|
86
94
|
has_rdoc:
|