Ruby方法和可选参数

时间:2012-04-19 18:24:26

标签: ruby-on-rails ruby

我正在使用Ruby on Rails,我正在尝试使用可选参数创建一个方法。显然,有很多方法可以做到这一点。我尝试将可选参数命名为哈希值,而不定义它们。输出是不同的。看看:

# 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!

可选参数的第二种方法有什么问题? 第二种方法只有在我没有传递任何可选参数或者我都传递它们时才有效。

我错过了什么?

11 个答案:

答案 0 :(得分:55)

可选参数在ruby中的工作方式是指定等号,如果没有传递参数,则 ,然后使用您指定的内容。所以,如果在第二个例子中没有传递第二个参数,那么

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

被使用。如果您在第一个参数之后使用哈希语法,则传递 exact 哈希。

你能做的最好的事情是

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

答案 1 :(得分:15)

问题是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的两种方法即可。

  

rails / activesupport / lib / active_support / core_ext / array /   extract_options.rb

因此,当您调用方法时,请调用它,就像使用其他任何其他Rails辅助方法一样,并使用其他选项。

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

答案 2 :(得分:9)

如果要默认选项哈希中的值,则需要合并函数中的默认值。如果您将默认值放在默认参数本身中,它将被覆盖:

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

  ...
end

答案 3 :(得分:5)

在第二种方法中,当你说,

my_info2 "Bill", age: 28

它将通过{age:28},并且将覆盖整个原始默认哈希{age:27,weight:160,city:“New York”}。这就是为什么它没有正确显示。

答案 4 :(得分:4)

您还可以使用关键字参数定义方法签名(New,因为这个问题很旧,因此,Ruby 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_food中收集的rest_of_options)在哈希中收集的额外值

答案 5 :(得分: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

答案 6 :(得分:2)

回答“为什么?”的问题:你调用函数的方式,

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

实际上在做

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

请注意,您传递了两个参数"Bill"和一个哈希对象,这将导致您在my_info2中提供的默认哈希值被完全忽略。

您应该使用其他回答者提到的默认值方法。

答案 7 :(得分:2)

#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

答案 8 :(得分:1)

使用或运算符设置默认值时,我没有看到任何错误。这是一个真实的例子(注意,使用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\" />" 

感觉类似于在调用类时使用initialize方法。

答案 9 :(得分:1)

为什么不使用nil?

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

答案 10 :(得分:0)

ActiveRecord模型上有一个method_missing方法,您可以覆盖它以让您的类直接动态响应调用。这是一个不错的blog post就可以了。