2010-08-31 34 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