[英]How do I recursively define a Hash in Ruby from supplied arguments?
This snippet of code populates an @options
hash. 这段代码填充了@options
哈希。 values
is an Array
which contains zero or more heterogeneous items. values
是一个包含零个或多个异构项的Array
。 If you invoke populate
with arguments that are Hash
entries, it uses the value you specify for each entry to assume a default value. 如果使用作为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
What I'd like to do is change populate
such that it recursively populates @options
. 我想做的是改变populate
,以便递归填充@options
。 There is a special case: if the values it's about to populate a key with are an Array consisting entirely of (1) Symbols or (2) Hashes whose keys are Symbols (or some combination of the two), then they should be treated as subkeys rather than the values associated with that key, and the same logic used to evaluate the original populate
arguments should be recursively re-applied. 有一种特殊情况:如果要填充键的值是一个完全由(1)符号组成的数组或(2)键是符号(或两者的某种组合)的哈希值,那么它们应该被视为子键而不是与该键关联的值,并且应该递归地重新应用用于评估原始populate
参数的相同逻辑。
That was a little hard to put into words, so I've written some test cases. 这有点难以言喻,所以我写了一些测试用例。 Here are some test cases and the expected value of @options
afterwards: 以下是一些测试用例和@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"
}
}
It is acceptable if the signature of populate
needs to change to accommodate some kind of recursive version. 如果populate
的签名需要改变以适应某种递归版本是可以接受的。 There is no limit to the amount of nesting that could theoretically happen. 理论上可以发生的嵌套量没有限制。
Any thoughts on how I might pull this off? 关于我如何解决这个问题的任何想法?
So here's some simple code that works. 所以这里有一些简单的代码可行。
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
It does deviate from your test cases: 它确实偏离了您的测试用例:
populate :a, :b => "apples", :c
is a ruby syntax error. 测试用例populate :a, :b => "apples", :c
是ruby语法错误。 Ruby will assume the final argument to a method is a hash (when not given braces), but not a non-final one, as you assume here. Ruby会假设方法的最后一个参数是一个哈希(当没有给出大括号时),但不是非最终的,正如你在这里假设的那样。 The given code is a syntax error (no matter the definition of populate
) since it assumes :c
is a hash key, and finds an end of line when it's looking for :c
's value. 给定的代码是语法错误(无论populate
的定义),因为它假定:c
是一个散列键,并在查找时找到行尾:c
的值。 populate :a, {:b => "apples"}, :c
works as expected populate :a, {:b => "apples"}, :c
按预期工作 populate :a => [:one], :b => [:two, :three => "four"]
returns {:a=>{:one=>{}}, :b=>{:two=>{}, :three=>"four"}}
. 测试用例populate :a => [:one], :b => [:two, :three => "four"]
返回{:a=>{:one=>{}}, :b=>{:two=>{}, :three=>"four"}}
。 This is consistent with the test case populate :a => [:b]
. 这与populate :a => [:b]
测试用例一致populate :a => [:b]
。 Ruby isn't Perl, =>
works only inside real Hash definition or as final argument in method call. Ruby不是Perl, =>
仅在真正的Hash定义内部工作或在方法调用中作为最终参数。 Most things you want will result in a syntax error. 您想要的大多数事情都会导致语法错误。
Are you sure that populate
limited to cases supported by Ruby syntax is worth it? 您确定仅限于Ruby语法支持的populate
值是否值得?
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.