2012-04-19 103 views
29

我在玩Ruby on Rails,我試圖用可選參數創建一個方法。顯然有很多方法可以做到這一點。我嘗試將可選參數命名爲散列,並且不定義它們。輸出是不同的。看看:Ruby方法和可選參數

# This functions works fine! 
def my_info(name, options = {}) 
    age = options[:age] || 27 
    weight = options[:weight] || 160 
    city = options[:city] || "New York" 
    puts "My name is #{name}, my age is #{age}, my weight is #{weight} and I live in {city}" 
end 


my_info "Bill" 
-> My name is Bill, my age is 27, my weight is 160 and I live in New York 
-> OK! 

my_info "Bill", age: 28 
-> My name is Bill, my age is 28, my weight is 160 and I live in New York 
-> OK! 

my_info "Bill", weight: 200 
-> My name is Bill, my age is 27, my weight is 200 and I live in New York 
-> OK! 

my_info "Bill", city: "Scottsdale" 
-> My name is Bill, my age is 27, my weight is 160 and I live in Scottsdale 
-> OK! 

my_info "Bill", age: 99, weight: 300, city: "Sao Paulo" 
-> My name is Bill, my age is 99, my weight is 300 and I live in Sao Paulo 
-> OK! 

**************************** 

# This functions doesn't work when I don't pass all the parameters 
def my_info2(name, options = {age: 27, weight: 160, city: "New York"}) 
    age = options[:age] 
    weight = options[:weight] 
    city = options[:city] 
    puts "My name is #{name}, my age is #{age}, my weight is #{weight} and I live in #{city}" 
end 

my_info2 "Bill" 
-> My name is Bill, my age is 27, my weight is 160 and I live in New York 
-> OK! 

my_info2 "Bill", age: 28 
-> My name is Bill, my age is 28, my weight is and I live in 
-> NOT OK! Where is my weight and the city?? 

my_info2 "Bill", weight: 200 
-> My name is Bill, my age is , my weight is 200 and I live in 
-> NOT OK! Where is my age and the city?? 

my_info2 "Bill", city: "Scottsdale" 
-> My name is Bill, my age is , my weight is and I live in Scottsdale 
-> NOT OK! Where is my age and my weight? 

my_info2 "Bill", age: 99, weight: 300, city: "Sao Paulo" 
-> My name is Bill, my age is 99, my weight is 300 and I live in Sao Paulo 
-> OK! 

可選參數的第二種方法有什麼問題嗎? 第二種方法只適用於如果我沒有傳遞任何可選參數或者如果我傳遞它們全部。

我錯過了什麼?

回答

52

方式可選參數在紅寶石的工作是你指定了一個等號,如果沒有參數傳遞然後你使用的是什麼。所以,如果第二個例子中沒有第二個參數,那麼

{age: 27, weight: 160, city: "New York"} 

被使用。如果在第一個參數後面使用哈希語法,那麼將傳遞該哈希值。

你能做的最好的是

def my_info2(name, options = {}) 
    options = {age: 27, weight: 160, city: "New York"}.merge(options) 
... 
+0

這是我的問題的解決方案。在閱讀@ texasbruce關於覆蓋原始散列的解釋之後,將「默認」散列與散列散列合併是合理的。謝謝! – 2012-04-19 18:42:26

+3

因爲'options'將被重新分配,所以你可以在原地進行更新:'options.reverse_update(年齡:27,體重:160,城市:「紐約」)' – tokland 2012-04-19 18:51:04

2

對於你的哈希的默認值。如果你想預設在選項的哈希值,你應該使用這個

def some_method(required_1, required_2, options={}) 
    defaults = { 
    :option_1 => "option 1 default", 
    :option_2 => "option 2 default", 
    :option_3 => "option 3 default", 
    :option_4 => "option 4 default" 
    } 
    options = defaults.merge(options) 

    # Do something awesome! 
end 
+2

如果使用合併()收集

  • 多餘的值在哈希()!你在這裏展示的方式,它產生和丟棄一個新的散列,而不是修改現有的選項散列。 – Winfield 2012-04-19 18:30:17

  • 9

    ,你想在你的功能合併爲默認值。如果你把默認參數本身的默認值,它會被覆蓋:

    def my_info(name, options = {}) 
        options.reverse_merge!(age: 27, weight: 160, city: "New York") 
    
        ... 
    end 
    
    +0

    這不會總是覆蓋默認值傳入的值嗎? – Christian 2012-04-19 18:34:30

    +0

    是的,應該是reverse_merge或更改合併的順序。 – Winfield 2012-04-19 18:40:28

    +0

    快速高效的解決方案。對我來說最好的答案。 +1 – konsolebox 2014-06-29 15:28:49

    4

    在第二個方法,當你說,

    my_info2 "Bill", age: 28

    它會通過{年齡:28},並且整個原始缺省散列{年齡:27,體重:160,城市:「紐約」}將被覆蓋。這就是爲什麼它不能正確顯示。

    +0

    這是最好的解釋。謝謝。 – 2012-04-19 18:40:37

    0

    ActiveRecord模型上有一個method_missing方法,您可以重寫該方法以使您的類動態直接響應調用。這是一個很好的blog post就可以了。

    1

    要回答的「爲什麼呢?」這樣的問題:你調用你的函數的方式,

    my_info "Bill", age: 99, weight: 300, city: "Sao Paulo" 
    

    其實就是做

    my_info "Bill", {:age => 99, :weight => 300, :city => "Sao Paulo"} 
    

    通知你逝去的兩個參數,"Bill"和散列對象,這會導致您在my_info2中提供的默認散列值被完全忽略。

    您應該使用其他答覆者提到的默認值方法。

    1

    #fetch是你的朋友!

    class Example 
        attr_reader :age 
        def my_info(name, options = {}) 
        @age = options.fetch(:age, 27) 
        self 
        end 
    end 
    
    person = Example.new.my_info("Fred") 
    puts person.age #27 
    
    14

    的問題是options默認值是在您發佈的第二個版本的整個Hash。所以,默認值,整個Hash,被覆蓋。這就是爲什麼不傳遞任何東西的原因,因爲這會激活默認值Hash,並且輸入它們全部也是可行的,因爲它會覆蓋默認值,其中包含相同密鑰的Hash

    我強烈建議使用Array來捕獲方法調用結束時的所有其他對象。

    def my_info(name, *args) 
        options = args.extract_options! 
        age = options[:age] || 27 
    end 
    

    我通過閱讀Rails的源代碼瞭解了這個技巧。但是,請注意,只有在包含ActiveSupport的情況下才有效。或者,如果您不想要整個ActiveSupport gem的開銷,只需使用添加到HashArray的兩種方法即可實現此功能。

    軌/的ActiveSupport/lib中/ active_support/core_ext /陣列/ extract_options.rb

    所以,當你打電話給你的方法,調用它就像你將任何其他Rails的helper方法提供了更多選擇。

    my_info "Ned Stark", "Winter is coming", :city => "Winterfell" 
    
    +1

    這是一個很棒的模式,謝謝。當你將這個與通過''scoped''' nil優雅失敗的鏈式範圍結合時,你可以做一些強大的查詢。 – Kelseydh 2015-04-02 11:03:25

    0

    我沒有看到使用or運算符設置默認值時出錯。這裏有一個現實生活中的例子(注意,使用Rails的image_tag法):

    文件:

    def gravatar_for(user, options = {}) 
        height = options[:height] || 90 
        width = options[:width] || 90 
        alt = options[:alt] || user.name + "'s gravatar" 
    
        gravatar_address = 'http://1.gravatar.com/avatar/' 
    
        clean_email = user.email.strip.downcase 
        hash = Digest::MD5.hexdigest(clean_email) 
    
        image_tag gravatar_address + hash, height: height, width: width, alt: alt 
    end 
    

    控制檯:

    2.0.0-p247 :049 > gravatar_for(user) 
    => "<img alt=\"jim&#39;s gravatar\" height=\"90\" src=\"http://1.gravatar.com/avatar/<hash>\" width=\"90\" />" 
    2.0.0-p247 :049 > gravatar_for(user, height: 123456, width: 654321) 
    => "<img alt=\"jim&#39;s gravatar\" height=\"123456\" src=\"http://1.gravatar.com/avatar/<hash>\" width=\"654321\" />" 
    2.0.0-p247 :049 > gravatar_for(user, height: 123456, width: 654321, alt: %[dogs, cats, mice]) 
    => "<img alt=\"dogs cats mice\" height=\"123456\" src=\"http://1.gravatar.com/avatar/<hash>\" width=\"654321\" />" 
    

    感覺類似於使用初始化方法時打電話給班級。

    0

    爲什麼不只是使用零?

    def method(required_arg, option1 = nill, option2 = nil) 
        ... 
    end 
    
    +0

    如果您打算將其作爲哈希進行訪問,哈希將會產生更好的默認值。如果它是一個空的散列,option1 [:undefined_key]將返回nil,但如果爲零,則會引發異常。 – jay 2015-02-19 06:31:09

    1

    您還可以定義關鍵字參數的方法簽名(新因爲,紅寶石2.0,因爲這個問題是舊的):

    def my_info2(name, age: 27, weight: 160, city: "New York", **rest_of_options) 
        p [name, age, weight, city, rest_of_options] 
    end 
    
    my_info2('Joe Lightweight', weight: 120, age: 24, favorite_food: 'crackers') 
    

    這允許以下:

    • 可選參數(:weight:age
    • 默認值
    • 的參數任意順序使用,而不是合併雙圖示(:favorite_foodrest_of_options收集)