2010-05-24 31 views
3

這段代碼填充@options散列。 values是一個Array其中包含零個或多個異構項目。如果您調用populate的參數爲Hash條目,它將使用您爲每個條目指定的值假定默認值。如何從提供的參數中遞歸地定義Ruby中的哈希值?

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)其鍵爲符號(或兩者的某種組合)組成的數組,則它們應被視爲子項而不是與該關鍵字相關的值,並且用於評估原始參數的相同邏輯應遞歸重新應用。

這有點難以表達出來,所以我寫了一些測試用例。下面是一些測試用例和@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簽名需要改變以適應某種遞歸版本。理論上可以發生的嵌套量沒有限制。

任何想法如何我可以把它關閉?

回答

1

所以這裏有一些簡單的代碼可以工作。

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會假設最後的參數爲一個方法是一個散列(當沒有給出大括號時),但不是非最終的,如你在這裏假設的那樣。給定的代碼是語法錯誤(無論populate的定義),因爲它假設:c是散列鍵,並且在查找:c的值時發現行末。 populate :a, {:b => "apples"}, :c按預期工作
  • 測試用例populate :a => [:one], :b => [:two, :three => "four"]返回{:a=>{:one=>{}}, :b=>{:two=>{}, :three=>"four"}}。這與測試案例populate :a => [:b]一致。
1

Ruby不是Perl,=>只能在真正的哈希定義內或作爲方法調用中的最後一個參數。你想要的大多數東西都會導致語法錯誤。

你確定populate僅限於受Ruby語法支持的情況嗎值得嗎?