繁体   English   中英

如何从提供的参数递归定义Ruby中的Hash?

[英]How do I recursively define a Hash in Ruby from supplied arguments?

这段代码填充了@options哈希。 values是一个包含零个或多个异构项的Array 如果使用作为Hash条目的参数调用populate ,它将使用您为每个条目指定的值来采用默认值。

def populate(*args)
  args.each do |a|
    values = nil
    if (a.kind_of? Hash)
      # Converts {:k => "v"} to `a = :k, values = "v"`
      a, values = a.to_a.first
    end

    @options[:"#{a}"] ||= values ||= {}
  end
end

我想做的是改变populate ,以便递归填充@options 有一种特殊情况:如果要填充键的值是一个完全由(1)符号组成的数组或(2)键是符号(或两者的某种组合)的哈希值,那么它们应该被视为子键而不是与该键关联的值,并且应该递归地重新应用用于评估原始populate参数的相同逻辑。

这有点难以言喻,所以我写了一些测试用例。 以下是一些测试用例和@options的预期值:

populate :a
=> @options is {:a => {}}

populate :a => 42
=> @options is {:a => 42}

populate :a, :b, :c
=> @options is {:a => {}, :b => {}, :c => {}}

populate :a, :b => "apples", :c
=> @options is {:a => {}, :b => "apples", :c => {}}

populate :a => :b
=> @options is {:a => :b}

# Because [:b] is an Array consisting entirely of Symbols or
# Hashes whose keys are Symbols, we assume that :b is a subkey
# of @options[:a], rather than the value for @options[:a].
populate :a => [:b]
=> @options is {:a => {:b => {}}}

populate :a => [:b, :c => :d]
=> @options is {:a => {:b => {}, :c => :d}}

populate :a => [:a, :b, :c]
=> @options is {:a => {:a => {}, :b => {}, :c => {}}}

populate :a => [:a, :b, "c"]
=> @options is {:a => [:a, :b, "c"]}

populate :a => [:one], :b => [:two, :three => "four"]
=> @options is {:a => :one, :b => {:two => {}, :three => "four"}}

populate :a => [:one], :b => [:two => {:four => :five}, :three => "four"]
=> @options is {:a => :one,
                :b => {
                   :two => {
                      :four => :five
                      }
                   },
                   :three => "four"
                }
               }

如果populate的签名需要改变以适应某种递归版本是可以接受的。 理论上可以发生的嵌套量没有限制。

关于我如何解决这个问题的任何想法?

所以这里有一些简单的代码可行。

def to_value args
  ret = {}
  # make sure we were given an array
  raise unless args.class == Array
  args.each do |arg|
    case arg
    when Symbol
      ret[arg] = {} 
    when Hash
      arg.each do |k,v|
        # make sure that all the hash keys are symbols
        raise unless k.class == Symbol
        ret[k] = to_value v 
      end           
    else    
      # make sure all the array elements are symbols or symbol-keyed hashes
      raise         
    end     
  end
  ret
rescue
  args
end
def populate *args
  @options ||= {}
  value = to_value(args)
  if value.class == Hash
    @options.merge! value
  end
end

它确实偏离了您的测试用例:

  • 测试用例populate :a, :b => "apples", :c是ruby语法错误。 Ruby会假设方法的最后一个参数是一个哈希(当没有给出大括号时),但不是非最终的,正如你在这里假设的那样。 给定的代码是语法错误(无论populate的定义),因为它假定:c是一个散列键,并在查找时找到行尾:c的值。 populate :a, {:b => "apples"}, :c按预期工作
  • 测试用例populate :a => [:one], :b => [:two, :three => "four"]返回{:a=>{:one=>{}}, :b=>{:two=>{}, :three=>"four"}} 这与populate :a => [:b]测试用例一致populate :a => [:b]

Ruby不是Perl, =>仅在真正的Hash定义内部工作或在方法调用中作为最终参数。 您想要的大多数事情都会导致语法错误。

您确定仅限于Ruby语法支持的populate值是否值得?

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM