我有一个类似以下的Ruby方法:
# Retrieve all fruits from basket that are of the specified kind.
def fruits_of_kind(kind)
basket.select { |f| f.fruit_type == kind.to_s }
end
现在,你可以这样称呼:
fruits_of_kind(:apple) # => all apples in basket
fruits_of_kind('banana') # => all bananas in basket
等等。
如何更改方法以便正确处理可迭代输入以及无输入和无输入?例如,我希望能够支持:
fruits_of_kind(nil) # => nil
fruits_of_kind(:apple, :banana) # => all apples and bananas in basket
fruits_of_kind([:apple, 'banana']) # => likewise
这可能是惯用的吗?如果是这样,那么编写方法的最佳方法是什么,以便它们可以接受零个,一个或多个输入?
答案 0 :(得分:3)
你需要使用Ruby splat运算符,它将所有剩余的参数包装到一个数组中并传入它们:
def foo (a, b, *c)
#do stuff
end
foo(1, 2) # a = 1, b = 2, c = []
foo(1, 2, 3, 4, 5) #a = 1, b = 2, c = [3, 4, 5]
在你的情况下,这样的事情应该有效:
def fruits_of_kind(*kinds)
kinds.flatten!
basket.select do |fruit|
kinds.each do |kind|
break true if fruit.fruit_type == kind.to_s
end == true #if no match is found, each returns the whole array, so == true returns false
end
end
我将代码更改为展平种类,以便您可以发送列表。此代码将处理任何类型的输入,但如果您想明确输入nil
,请在开头添加行kinds = [] if kinds.nil?
。
答案 1 :(得分:2)
使用Ruby的VARARGS功能。
# Retrieve all fruits from basket that are of the specified kind.
# notice the * prefix used for method parameter
def fruits_of_kind(*kind)
kind.each do |x|
puts x
end
end
fruits_of_kind(:apple, :orange)
fruits_of_kind()
fruits_of_kind(nil)
-sasuke
答案 2 :(得分:1)
def fruits_of_kind(kind)
return nil if kind.nil?
result = []
([] << kind).flatten.each{|k| result << basket.select{|f| f.fruit_type == k.to_s }}
result
end
'splat'运算符可能是最好的方法,但有两点需要注意:传入nil或列表。要修改Pesto的输入/输出解决方案,您应该执行以下操作:
def fruits_of_kind(*kinds)
return nil if kinds.compact.empty?
basket.select do |fruit|
kinds.flatten.each do |kind|
break true if fruit.fruit_type == kind.to_s
end == true #if no match is found, each returns the whole array, so == true returns false
end
end
如果传入nil,*将其转换为[nil]。如果你想返回nil而不是空列表,你必须将它(删除空值)压缩为[],然后如果它为空则返回nil。
如果您传入一个列表,例如[:apple,'banana'],*会将其转换为[[:apple,'banana']]。这是一个细微的差别,但它是一个包含另一个列表的单元素列表,因此您需要在执行“每个”循环之前展平各种类型。展平将把它转换为[:apple,'banana'],就像你期望的那样,并为你提供你想要的结果。
编辑:更好,感谢Greg Campbell:
def fruits_of_kind(basket, kind)
return nil if kind.nil?
kind_list = ([] << kind).flatten.map{|kind| kind.to_s}
basket.select{|fruit| kind_list.include?(fruit) }
end
OR(使用splat)
def fruits_of_kind(*kinds)
return nil if kinds.compact.empty?
kind_list = kinds.flatten.map{|kind| kind.to_s}
basket.select{|fruit| kind_list.include?(fruit.fruit_type) }
end
答案 3 :(得分:0)
有一个很好的表达方式使用splat作为数组创建的参数来处理你的上一个例子:
def foo(may_or_may_not_be_enumerable_arg)
arrayified = [*may_or_may_not_be_enumerable_arg]
arrayified.each do |item|
puts item
end
end
obj = "one thing"
objs = ["multiple", "things", 1, 2, 3]
foo(obj)
# one thing
# => ["one thing"]
foo(objs)
# multiple
# things
# 1
# 2
# 3
# => ["multiple", "things", 1, 2, 3]