Как мне написать метод Ruby для обработки нуля, одного или нескольких входов? - PullRequest
4 голосов
/ 08 мая 2009

У меня есть метод 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

Возможно ли это сделать идиоматически? Если да, то как лучше написать методы, чтобы они могли принимать ноль, один или несколько входных данных?

Ответы [ 4 ]

3 голосов
/ 08 мая 2009

Вам нужно использовать оператор 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

EDIT

Я изменил код, чтобы выровнять виды, чтобы вы могли отправлять их в виде списка. Этот код будет обрабатывать ввод вообще без видов, но если вы хотите явно ввести nil, добавьте строку kinds = [] if kinds.nil? в начале.

2 голосов
/ 08 мая 2009

Используйте функцию VARARGS в Ruby.

# 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

1 голос
/ 08 мая 2009
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 или списков. Чтобы изменить решение Песто для ввода / вывода, который вам нужен, вы должны сделать что-то вроде этого:

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'], как вы ожидаете, и даст вам результаты, которые вы ищете.

РЕДАКТИРОВАТЬ : еще лучше, благодаря Грегу Кэмпбеллу:

   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
0 голосов
/ 08 января 2014

Существует довольно выразительное использование 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]
...