added a binary tree storage
This commit is contained in:
parent
61b962fea3
commit
1d2035c637
|
@ -0,0 +1,136 @@
|
||||||
|
module Polecat
|
||||||
|
module Storage
|
||||||
|
# This class is a storage engine based on a simple hash.
|
||||||
|
class BinaryStorage < Storage
|
||||||
|
def initialize
|
||||||
|
@root = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
# add a new key value pair
|
||||||
|
def add key, value = nil
|
||||||
|
if key.class == Node
|
||||||
|
new_node = key
|
||||||
|
else
|
||||||
|
check_key key
|
||||||
|
new_node = Node.new key, value
|
||||||
|
end
|
||||||
|
|
||||||
|
if @root.nil?
|
||||||
|
@root = new_node
|
||||||
|
else
|
||||||
|
cur_node = @root
|
||||||
|
while cur_node.lower != new_node && cur_node.upper != new_node
|
||||||
|
if cur_node.key > new_node.key
|
||||||
|
if cur_node.lower.nil?
|
||||||
|
cur_node.lower = new_node
|
||||||
|
else
|
||||||
|
cur_node = cur_node.lower
|
||||||
|
end
|
||||||
|
elsif cur_node.key <= new_node.key
|
||||||
|
if cur_node.upper.nil?
|
||||||
|
cur_node.upper = new_node
|
||||||
|
else
|
||||||
|
cur_node = cur_node.upper
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end # end of while
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def delete key
|
||||||
|
check_key key
|
||||||
|
|
||||||
|
cur_node = @root
|
||||||
|
parent = nil
|
||||||
|
while !cur_node.nil?
|
||||||
|
if cur_node.key == key
|
||||||
|
if parent.nil?
|
||||||
|
@root = nil
|
||||||
|
else
|
||||||
|
if parent.lower == cur_node
|
||||||
|
parent.lower = nil
|
||||||
|
else
|
||||||
|
parent.upper = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
add cur_node.lower unless cur_node.lower.nil?
|
||||||
|
add cur_node.upper unless cur_node.upper.nil?
|
||||||
|
return cur_node.value
|
||||||
|
else
|
||||||
|
if cur_node.key < key
|
||||||
|
parent = cur_node
|
||||||
|
cur_node = cur_node.upper
|
||||||
|
else
|
||||||
|
parent = cur_node
|
||||||
|
cur_node = cur_node.lower
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def interval lower, upper, node = @root
|
||||||
|
check_key lower
|
||||||
|
check_key upper
|
||||||
|
|
||||||
|
select do |key|
|
||||||
|
lower <= key && key <= upper
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def find key
|
||||||
|
check_key key
|
||||||
|
|
||||||
|
cur_node = @root
|
||||||
|
until cur_node.nil?
|
||||||
|
if cur_node.key == key
|
||||||
|
return cur_node.value
|
||||||
|
elsif
|
||||||
|
if cur_node.key > key
|
||||||
|
cur_node = cur_node.lower
|
||||||
|
else
|
||||||
|
cur_node = cur_node.upper
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def select node = @root, &block
|
||||||
|
if node.nil?
|
||||||
|
[]
|
||||||
|
else
|
||||||
|
rs = []
|
||||||
|
rs << node.value if block.call(node.key)
|
||||||
|
rs += (select node.lower, &block)
|
||||||
|
rs += (select node.upper, &block)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def count node = @root
|
||||||
|
if node.nil?
|
||||||
|
0
|
||||||
|
else
|
||||||
|
1 + (count node.lower) + (count node.upper)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def check_key key
|
||||||
|
unless key.respond_to?(:<=>) && key.respond_to?(:<=)
|
||||||
|
raise ArgumentError, 'key does not support #<=>'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
private :check_key
|
||||||
|
|
||||||
|
class Node
|
||||||
|
attr_accessor :value, :lower, :upper
|
||||||
|
attr_reader :key
|
||||||
|
def initialize key, value
|
||||||
|
@key = key
|
||||||
|
@value = value
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,22 @@
|
||||||
|
require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
|
||||||
|
require 'polecat/storage/binary_storage'
|
||||||
|
|
||||||
|
describe "BinaryStorage#add" do
|
||||||
|
let (:s) { Polecat::Storage::BinaryStorage.new }
|
||||||
|
|
||||||
|
it "raises the element count" do
|
||||||
|
s.add('foo', 'bar')
|
||||||
|
s.count.should == 1
|
||||||
|
end
|
||||||
|
|
||||||
|
it "raises the element count with every add" do
|
||||||
|
s.add('foo', 1)
|
||||||
|
s.add('bar', 2)
|
||||||
|
s.add('baz', 3)
|
||||||
|
s.count.should == 3
|
||||||
|
end
|
||||||
|
|
||||||
|
it "raises an error when the attribute does not support #<=>" do
|
||||||
|
lambda { s.add(nil, 'baz') }.should raise_error(ArgumentError)
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,28 @@
|
||||||
|
require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
|
||||||
|
require 'polecat/storage/binary_storage'
|
||||||
|
|
||||||
|
describe "BinaryStorage#delete" do
|
||||||
|
let (:s) { Polecat::Storage::BinaryStorage.new }
|
||||||
|
before do
|
||||||
|
s.add 'foo', 'bar'
|
||||||
|
s.add 'bar', 'baz'
|
||||||
|
s.add 'baz', 'foo'
|
||||||
|
end
|
||||||
|
|
||||||
|
it "decrements the element count" do
|
||||||
|
s.delete('foo')
|
||||||
|
s.count.should == 2
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns the value of the deleted key" do
|
||||||
|
s.delete('foo').should == 'bar'
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns nil when the element was not found" do
|
||||||
|
s.delete('foobar').should be(nil)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "raises an error when the argument does not support #<=>" do
|
||||||
|
lambda { s.delete(:foo) }.should raise_error(ArgumentError)
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,24 @@
|
||||||
|
require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
|
||||||
|
require 'polecat/storage/binary_storage'
|
||||||
|
|
||||||
|
describe "BinaryStorage#find" do
|
||||||
|
let (:s) { Polecat::Storage::BinaryStorage.new }
|
||||||
|
before do
|
||||||
|
s.add 'foo', 'bar'
|
||||||
|
s.add 'bar', 'baz'
|
||||||
|
s.add 'baz', 'foobar'
|
||||||
|
s.add 'zum', 'zum'
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns nil when the element was not found" do
|
||||||
|
s.find('baba').should be(nil)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns the value of the key" do
|
||||||
|
s.find('foo').should == 'bar'
|
||||||
|
end
|
||||||
|
|
||||||
|
it "raises an error when the type does not know #<=>" do
|
||||||
|
lambda { s.find(:foo) }.should raise_error(ArgumentError)
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,28 @@
|
||||||
|
require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
|
||||||
|
require 'polecat/storage/binary_storage'
|
||||||
|
|
||||||
|
describe "BinaryStorage#interval" do
|
||||||
|
let (:s) { Polecat::Storage::BinaryStorage.new }
|
||||||
|
before do
|
||||||
|
s.add 'foo', 'bar'
|
||||||
|
s.add 'bar', 'baz'
|
||||||
|
s.add 'baz', 'foobar'
|
||||||
|
s.add 'zum', 'zum'
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns an empty array when no element was found" do
|
||||||
|
s.interval('a', 'b').should == []
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns an array of found elements" do
|
||||||
|
s.interval('ba', 'bb').sort.should == ['baz', 'foobar']
|
||||||
|
end
|
||||||
|
|
||||||
|
it "includes the search values" do
|
||||||
|
s.interval('bar', 'baz').sort.should == ['baz', 'foobar']
|
||||||
|
end
|
||||||
|
|
||||||
|
it "raises an error when one of the arguments does not implement #<=>" do
|
||||||
|
lambda { s.interval(1, :foo) }.should raise_error(ArgumentError)
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,10 @@
|
||||||
|
require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
|
||||||
|
require 'polecat/storage/binary_storage'
|
||||||
|
|
||||||
|
describe "BinaryStorage#new" do
|
||||||
|
it "creates a new storage object" do
|
||||||
|
s = Polecat::Storage::BinaryStorage.new
|
||||||
|
s.kind_of?(Polecat::Storage::Storage).should == true
|
||||||
|
s.class.should be(Polecat::Storage::BinaryStorage)
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,25 @@
|
||||||
|
require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
|
||||||
|
require 'polecat/storage/binary_storage'
|
||||||
|
|
||||||
|
describe "BinaryStorage#select" do
|
||||||
|
let (:s) { Polecat::Storage::BinaryStorage.new }
|
||||||
|
before do
|
||||||
|
s.add 'key', 'value'
|
||||||
|
s.add 'foo', 'bar'
|
||||||
|
s.add 'bar', 'baz'
|
||||||
|
s.add 'baz', 'foobar'
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns an empty array, when no element was selected" do
|
||||||
|
(s.select {|element| false }).should == []
|
||||||
|
end
|
||||||
|
|
||||||
|
it "takes a block as an argument" do
|
||||||
|
(s.select {|element| element.match /ba*/ }).sort.should == ['baz', 'foobar']
|
||||||
|
end
|
||||||
|
|
||||||
|
it "does not handle errors" do
|
||||||
|
lambda { s.select {|element| raise ArgumentError }}.should(
|
||||||
|
raise_error(ArgumentError))
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in New Issue