Ruby 方法和可选参数
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/10234406/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Ruby Methods and Optional parameters
提问by Guillermo Guerini
I'm playing with Ruby on Rails and I'm trying to create a method with optional parameters. Apparently there are many ways to do it. I trying naming the optional parameters as hashes, and without defining them. The output is different. Take a look:
我正在使用 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!
What's wrong with the second approach for optional parameters? The second method only works if I don't pass any optional parameter or if I pass them all.
可选参数的第二种方法有什么问题?第二种方法仅在我不传递任何可选参数或全部传递时才有效。
What am I missing?
我错过了什么?
回答by Marlin Pierce
The way optional arguments work in ruby is that you specify an equal sign, and if no argument is passedthen what you specified is used. So, if no second argument is passed in the second example, then
可选参数在 ruby 中的工作方式是您指定一个等号,如果没有传递参数,则使用您指定的内容。所以,如果在第二个例子中没有传递第二个参数,那么
{age: 27, weight: 160, city: "New York"}
is used. If you do use the hash syntax after the first argument, then that exacthash is passed.
用来。如果您在第一个参数后使用哈希语法,则传递精确的哈希。
The best you can do is
你能做的最好的是
def my_info2(name, options = {})
options = {age: 27, weight: 160, city: "New York"}.merge(options)
...
回答by Charles Caldwell
The problem is the default value of optionsis the entire Hashin the second version you posted. So, the default value, the entire Hash, gets overridden. That's why passing nothing works, because this activates the default value which is the Hashand entering all of them also works, because it is overwriting the default value with a Hashof identical keys.
问题是您发布的第二个版本中的默认值options是整个Hash。因此,默认值整个Hash被覆盖。这就是为什么不传递任何东西的原因,因为这会激活默认值,Hash并且输入所有这些也有效,因为它用Hash相同的键覆盖了默认值。
I highly suggest using an Arrayto capture all additional objects that are at the end of your method call.
我强烈建议使用 anArray来捕获方法调用结束时的所有其他对象。
def my_info(name, *args)
options = args.extract_options!
age = options[:age] || 27
end
I learned this trick from reading through the source for Rails. However, note that this only works if you include ActiveSupport. Or, if you don't want the overhead of the entire ActiveSupport gem, just use the two methods added to Hashand Arraythat make this possible.
我从阅读 Rails 的源代码中学到了这个技巧。但是,请注意,这仅在您包含 ActiveSupport 时才有效。或者,如果你不希望整个的ActiveSupport宝石的开销,只是用加入到这两种方法Hash,并Array使这成为可能。
rails / activesupport / lib / active_support / core_ext / array / extract_options.rb
rails/activesupport/lib/active_support/core_ext/array/extract_options.rb
So when you call your method, call it much like you would any other Rails helper method with additional options.
因此,当您调用您的方法时,请像调用任何其他带有附加选项的 Rails 辅助方法一样调用它。
my_info "Ned Stark", "Winter is coming", :city => "Winterfell"
回答by Winfield
If you want to default the values in your options hash, you want to merge the defaults in your function. If you put the defaults in the default parameter itself, it'll be over-written:
如果要在选项哈希中默认值,则需要合并函数中的默认值。如果您将默认值放在默认参数本身中,它将被覆盖:
def my_info(name, options = {})
options.reverse_merge!(age: 27, weight: 160, city: "New York")
...
end
回答by texasbruce
In second approach, when you say,
在第二种方法中,当你说,
my_info2 "Bill", age: 28
my_info2 "Bill", age: 28
It will pass {age: 28}, and entire original default hash {age: 27, weight: 160, city: "New York"} will be overridden. That's why it does not show properly.
它将通过 {age: 28},并且整个原始默认哈希 {age: 27, weight: 160, city: "New York"} 将被覆盖。这就是它不能正确显示的原因。
回答by Henry Tseng
You can also define method signatures with keyword arguments (New since, Ruby 2.0, since this question is old):
您还可以使用关键字参数定义方法签名(自 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')
This allows for the following:
这允许以下内容:
- Optional parameters (
:weightand:age) - Default values
- Arbitrary order of parameters
- Extra values collected in a hash using double splat (
:favorite_foodcollected inrest_of_options)
- 可选参数(
:weight和:age) - 默认值
- 参数的任意顺序
- 使用 double splat 在散列中收集的额外值(
:favorite_food收集在 中rest_of_options)
回答by Abe Voelker
To answer the question of "why?": the way you're calling your function,
要回答“为什么?”的问题:您调用函数的方式,
my_info "Bill", age: 99, weight: 300, city: "Sao Paulo"
is actually doing
实际上在做
my_info "Bill", {:age => 99, :weight => 300, :city => "Sao Paulo"}
Notice you are passing two parameters, "Bill"and a hash object, which will cause the default hash value you've provided in my_info2to be completely ignored.
请注意,您正在传递两个参数"Bill"和一个哈希对象,这将导致您提供的默认哈希值my_info2被完全忽略。
You should use the default value approach that the other answerers have mentioned.
您应该使用其他回答者提到的默认值方法。
回答by Jesse Wolgamott
#fetchis your friend!
#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
回答by Christian
For the default values in your hash you should use this
对于散列中的默认值,您应该使用它
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
回答by Starkers
I don't see anything wrong with using an or operator to set defaults. Here's a real life example (note, uses rails' image_tagmethod):
我认为使用 or 运算符设置默认值没有任何问题。这是一个真实的例子(注意,使用 rails 的image_tag方法):
file:
文件:
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
console:
安慰:
2.0.0-p247 :049 > gravatar_for(user)
=> "<img alt=\"jim'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'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\" />"
It feels similar to using the initialize method when calling a class.
感觉和调用类时使用 initialize 方法类似。
回答by LLL
Why not just use nil?
为什么不直接使用 nil?
def method(required_arg, option1 = nil, option2 = nil)
...
end

