4

次のような 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

等々。

入力なしや nil 入力だけでなく、反復可能な入力も正しく処理するようにメソッドを変更するにはどうすればよいですか? たとえば、次のことをサポートできるようにしたいと考えています。

fruits_of_kind(nil) # => nil
fruits_of_kind(:apple, :banana)    # => all apples and bananas in basket
fruits_of_kind([:apple, 'banana']) # => likewise

これは慣用的に行うことは可能ですか?もしそうなら、ゼロ、1、または多くの入力を受け入れることができるようにメソッドを書くための最良の方法は何ですか?

4

4 に答える 4

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?

于 2009-05-08T15:18:19.557 に答える
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)

-サスケ

于 2009-05-08T15:08:51.873 に答える
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'演算子はおそらく最善の方法ですが、注意すべき点が2つあります。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を返したい場合は、リストを[]に圧縮(nullを削除)し、空の場合はnilを返す必要があります。

[:apple、'banana']のようにリストを渡すと、*はそれを[[:apple、'banana']]に変換します。微妙な違いですが、別のリストを含む1つの要素のリストであるため、「各」ループを実行する前に種類をフラット化する必要があります。平坦化すると、期待どおりに[:apple、'banana']に変換され、探している結果が得られます。

編集:グレッグキャンベルのおかげでさらに良い:

   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

または(スプラットを使用)

   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
于 2009-05-08T15:17:50.650 に答える
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]
于 2014-01-07T23:15:33.250 に答える