这个类似哈希表/树形结构的构造叫什么?

6
我将创建一个“Config”类,它在哈希表和树之间起到作用。它只是用于存储具有上下文的全局值。
以下是我的使用方式:
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" => {}}}
5个回答

9

您可以很快地实现其中一个:

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

这个版本有一些取舍,因为你不能在非叶节点中存储值。


有什么想法可以实现以下两点:a)使叶节点不是哈希值,而是实际的数值(这基本上意味着删除“attr_accessor:value”),b)如果“get(“root”)”或任何级别不是叶节点,则返回下面的树而不是null?我正在尝试实现它,但这会增加很多代码/复杂性,也许您知道一些一行代码的技巧。我已经更新了问题并提供了示例输出。 - Lance
嗨,这太棒了,应该包含在Ruby核心中!然而,在使用第二种实现时,在非叶节点上我的yaml输出中出现了"!ruby/hash:TreeHash"。有没有任何非gsub方法来去除它?谢谢! - Dr. Chocolate

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。 另一个问题的答案建议使用Hashie gem将JSON对象转换为Ruby对象。

0

我认为这类似于Java中描述的TreeMap数据结构(此处)。它执行相同的操作(键/值映射),但检索可能不同,因为您正在使用节点本身作为键。从所描述的TreeMap中检索是从实现中抽象出来的,因为当您传入一个键时,您不知道它在树中的确切位置。

希望这有意义!


0

嗯...使用分层哈希表是可以做到的,但你为什么需要这种层次结构呢?如果你只需要精确匹配获取和放置操作,为什么不能只使用一个单一哈希表并采用点分隔命名约定呢?

这就是实现你所需的功能所需要的全部内容,很明显非常简单...


我希望能够使用yaml定义所有内容,并能够检索任何级别的子节点,但同时也能将其存储在数据库中并让最终用户自定义它。所以最终它是一棵树,对吧? - Lance

0
为什么要使用类似哈希表的接口呢?为什么不使用方法链来导航您的树呢?例如,config.root.parent.child_b并使用实例方法,如果需要,可以使用method_missing()来实现它们?

太复杂了,我希望任何经验水平的人都能使用它。使用点分隔符的哈希表易于理解。嵌套对象/类更加复杂,对于新手来说很难自定义。 - Lance

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接