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