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