2010-06-05 86 views
6

我想创建一个“配置”类,在散列和树之间的某个地方起作用。它只是用于存储可以具有上下文的全局值。这是什么样的哈希式/树状构造调用?

下面是我如何使用它:

Config.get("root.parent.child_b") #=> "value" 

这里的类可能是什么样子:

class Construct 

    def get(path) 
    # split path by "." 
    # search tree for nodes 
    end 

    def set(key, value) 
    # split path by "." 
    # create tree node if necessary 
    # set tree value 
    end 

    def tree 
    { 
     :root => { 
     :parent => { 
      :child_a => "value", 
      :child_b => "another value" 
     }, 
     :another_parent => { 
      :something => { 
      :nesting => "goes on and on" 
      } 
     } 
     } 
    } 
    end 

end 

是否有这种东西的名称,哈希和树之间的某个地方(而不是计算机科学专业)?基本上是一个类似树的接口。

东西输出是这样的:

t = TreeHash.new 
t.set("root.parent.child_a", "value") 
t.set("root.parent.child_b", "another value") 

所需的输出格式:

t.get("root.parent.child_a") #=> "value" 
t.get("root") #=> {"parent" => {"child_a" => "value", "child_b" => "another value"}} 

,而不是这样的:

t.get("root") #=> nil 

或本(你从通过调用获得的价值{}.value

t.get("root") #=> {"parent" => {"child_a" => {}, "child_b" => {}}} 

回答

8

您可以实现一个在任何时间:

class TreeHash < Hash 
    attr_accessor :value 

    def initialize 
    block = Proc.new {|h,k| h[k] = TreeHash.new(&block)} 
    super &block 
    end 

    def get(path) 
    find_node(path).value 
    end 

    def set(path, value) 
    find_node(path).value = value 
    end 

private 

    def find_node(path) 
    path.split('.').inject(self){|h,k| h[k]} 
    end 
end 

您可以通过设置不需要Hash方法作为一个私立的提高实现的,但它已经运作的,你想要的方式。数据以散列形式存储,因此您可以轻松将其转换为yaml。


编辑:

为了满足进一步的预期(和默认情况下正确地转换to_yaml),你应该使用修改后的版本:

class TreeHash < Hash 
    def initialize 
    block = Proc.new {|h,k| h[k] = TreeHash.new(&block)} 
    super &block 
    end 

    def get(path) 
    path.split('.').inject(self){|h,k| h[k]} 
    end 

    def set(path, value) 
    path = path.split('.') 
    leaf = path.pop 
    path.inject(self){|h,k| h[k]}[leaf] = value 
    end 
end 

这个版本是轻微的权衡,因为你无法将值存储在非叶节点中。

+0

这真棒。 – 2010-06-05 22:48:41

+0

任何想法如何添加a)所以叶节点不是哈希值,它们是值(基本上意味着除去'attr_accessor:value'),和b)所以'get(“root”)'或任何级别返回下面的树而不是null如果它不是叶节点?试图实现这一点,但它增加了很多代码/复杂性,也许你知道一些内线技巧。我已经用示例输出更新了这个问题。 – 2010-06-05 23:30:52

+0

据我看到它是删除代码,而不是添加:) – samuil 2010-06-06 08:28:18

0

我认为这类似于描述here所描述的Java中的TreeMap数据结构。它做同样的事情(键/值映射),但由于您将节点本身用作键,检索可能会有所不同。从所描述的TreeMap中检索是从实现中抽象出来的,因为当你传入一个密钥时,你不知道它在树中的确切位置。

希望有道理!

0

呃......当然可以使用分层哈希表来完成,但您为什么需要层次结构?如果你只需要完全匹配的get和put,为什么你不能只使用一个单独的散列表来使用点分隔的命名约定呢?

这是所有对实现你要求的功能性需要,它显然很简单...

+0

我希望能够使用yaml定义所有这些,并且能够在任何级别检索子节点,但同时能够将其存储在数据库中并让最终用户对其进行定制。所以它最终是一棵树,最后不是? – 2010-06-05 08:17:07

0

为什么使用哈希一样的界面呢?为什么不使用方法链来浏览你的树?例如config.root.parent.child_b并使用实例方法,如果需要method_missing()来实现它们?

+0

太复杂了,我希望任何有经验的人都可以使用它。用点分隔的字符串散列很容易理解。嵌套对象/类更复杂,难以为新来者定制。 – 2010-06-05 09:38:38

1

我认为该结构的名称实际上是嵌套散列,而问题中的代码是JavaScript字典的重新创建。由于JS(或Python或...)中的字典可以嵌套,因此每个值都可以是另一个字典,它具有自己的键/值对。在JavaScript中,这是一个对象。

最妙的是能够使用JSON整齐地定义它,并围绕它传递:

tree : { 
    'root' : { 
    'parent' : { 
     'child_a' : "value", 
     'child_b' : "another value" 
    }, 
    'another_parent' : { 
     'something' : { 
     'nesting' : "goes on and on" 
     } 
    } 
    } 
}; 

在JS那么你可以做tree.root.parent.child_a。

This answer to another question建议使用Hashie gem将JSON对象转换为Ruby对象。