[英]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.