2010-08-31 35 views
3

有什么像boost :: multi_index但是对于ruby。基本上采取一些容器的对象,并使用N种不同的查询方法对N个不同的方式编制索引。ruby​​的多索引容器

我想你可以在内存数据库中使用SQLite的DataMapper,但我想知道是否有任何纯粹的红宝石。

下面是这种类型可能做的一个想象的例子。它看起来非常像数据库,非常类似于 。

class Foo 
    attr_accessor :a 
    attr_accessor :b 
    attr_accessor :c 
end 


class FooIndexer < MultiIndex 
    hash_index :a do |o| 
     o.a 
    end 

    ordered_index :b do |x, y| 
     x.b <=> y.b 
    end 
end 


index = FooIndexer.new 

index.insert(Foo.new (...)) 
index.insert(Foo.new (...)) 
index.insert(Foo.new (...)) 
index.insert(Foo.new (...)) 
index.insert(Foo.new (...)) 


index.find (index.a == 10) 
index.find (index.b > 10 ) 
+0

也许你可以举一个boost :: multi_index的例子用例吗? – AboutRuby 2010-09-01 01:07:12

回答

-1

这听起来像是你在实现此功能的特定方式之后。但是就红宝石般的接口而言,我会推荐使用Enumerable#find方法。这样,你可以说

foo_container = [FooIndexer.new, ...] 
foo_container.find{|x| x.a == 10} 

它看起来非常像你的例子,除了括号而不是括号!

后来,如果您发现性能很差,您可能想要进行某种缓存或优化find。但是,仅根据您的问题,如果您现在查找该问题,您将尽快进行优化。

Enumerable提供了大量的这些事情了,所以你有一个像

foo_container.select{|x| x.a == 10} # Finds all instances. 
foo_container.reject{|x| x.a == 10} # Finds the complementary set. 
+0

当然可以使用,但这不是真正的问题。枚举是伟大的,我的任何代码的核心组件,但我特别寻找一个容器,可以索引多个键。 – bradgonesurfing 2010-09-01 06:28:39

+0

当然,但为什么?你目前是否遇到性能问题?这将直接解决... – Peter 2010-09-01 07:10:03

+0

Cmon老兄!不要只是因为数组和Enumerable能够完成这项工作而警告我使用哈希值的人。如果数组中有100k个元素(也许我可能不这样做),那么使用Enumerable :: find与哈希查找进行线性搜索将会导致您失败。这就是Ruby提供哈希的原因。在一般的哈希中,数组和Enumerable提供了99%的算法需求。但是我问了一个具体的问题。看起来答案是否定的,如果我关心我可能会写我自己的版本,或者可能像我第一次建议的那样,将DataMapper与内存数据库中的SQLite结合使用。 – bradgonesurfing 2010-09-01 07:30:51

1

这是一个完全的工作方案,包括规范,但仅适用于 多个哈希键自然延伸。

require 'pp' 

class MKey 
    def initialize &bk 
    @block = bk 
    @containers = {} 
    end 

    def <<(val) 
    keys = @block.call(val) 
    keys.each do |k,v| 
     @containers[k] ||= {} 
     @containers[k][v] = val 
    end 
    end 

    def [](key) 
    k, v = key.first 
    @containers[k][v] 
    end 

    def delete(key) 
    val = self[key] 
    keys = @block.call(val) 
    keys.each do |k,v| 
     @containers[k].delete(v) 
    end 
    end 

    include Enumerable 

    def each 
    k, c = @containers.first 
    c.each do |k, val| 
     yield val 
    end 
    end 

end 


describe MKey do 

    class Foo 
    def initialize(a,b) 
     @a = a 
     @b = b 
    end 
    attr_accessor :a 
    attr_accessor :b 
    end 

    it "should insert" do 

    index = MKey.new do |o| 
     { :a => o.a, 
     :b => o.b 
     } 
    end 

    x = Foo.new("hello", "cat") 
    y = Foo.new("goodbye", "code") 

    index << x 
    index << y 

    # Test Enumerable interface 
    index.find do |val| 
     val.a == "hello" 
    end.should == x 

    # Test multi key interface 
    index[:a => "hello"].should == x 
    index[:b => "code"].should == y 

    index.delete(:a => "hello") 

    index[:a => "hello"].should == nil 
    index[:b => "code"].should == y 

    index.delete(:b => "code") 

    index[:a => "hello"].should == nil 
    index[:b => "code"].should == nil 


    end 

    it "hash lookup should be faster than find" do 


    index = MKey.new do |o| 
     { :a => o.a, 
     :b => o.b 
     } 
    end 

    for i in 1..10000 
     index << Foo.new(i, i*100) 
    end 

    t0 = timer do 
     index[:a => 1000] 
    end 

    t1 = timer do 
     index.find {|v| v.a == 10000} 
    end 

    t0.should < t1 * 100 

    end 

end