【问题标题】:Multi-index container for rubyruby 的多索引容器
【发布时间】:2010-08-31 19:43:21
【问题描述】:

除了 ruby​​ 之外,有没有类似 boost::multi_index 的东西。基本上拿了一些 对象容器,并使用 N 种不同的查询方法对 N 种不同的方法进行索引。

我猜你可以将 DataMapper 与内存数据库中的 SQLite 一起使用,但我是 想知道周围有没有纯红宝石。

下面是一个想象的例子,说明这种类型的类可能会做什么。看起来很 很像一个数据库。

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  )

【问题讨论】:

  • 也许你可以举一个 boost::multi_index 的例子?

标签: ruby boost multi-index


【解决方案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

【讨论】:

    【解决方案2】:

    听起来您正在寻找实现此功能的特定方式。但就类似于红宝石的界面而言,我建议只使用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.
    

    【讨论】:

    • 当然可以使用 find 但这不是真正的问题。 Enumerable 很棒,它是我的任何代码的核心组件,但我特意寻找一个可以按多个键索引的容器。
    • 当然,但是为什么呢?您目前是否遇到性能问题?这将指导解决方案...
    • 老兄!不要因为数组和 Enumerable 可以很好地完成工作而警告我不要使用哈希。如果数组中有 100k 个元素(也许我有,也许我没有),那么使用 Enumerable::find 的线性搜索与散列查找将杀死你。这就是 Ruby 提供哈希的原因。一般来说,散列、数组和 Enumerable 提供了 99% 的算法需求。但是我问了一个具体的问题。看起来答案是否定的,如果我愿意,我可能会编写自己的版本,或者像我最初建议的那样将 DataMapper 与内存数据库中的 SQLite 一起使用。
    • 实际上,我最终为 boost::multi-index 可以做的有限子集编写了自己的解决方案。见stackoverflow.com/questions/3612598/…
    猜你喜欢
    • 1970-01-01
    • 2013-11-08
    • 1970-01-01
    • 1970-01-01
    • 2011-09-04
    • 2011-07-12
    • 2013-11-07
    • 1970-01-01
    • 2014-12-15
    相关资源
    最近更新 更多