Возврат значения блока по перечислению, если оно не равно нулю

Кажется, у меня есть немного странный вариант использования Ruby Enumerable. Я пытаюсь сделать что-то вроде следующего:

result = my_strategies.some_method do |strategy|
    strategy.get_result
end

Метод some_method — это просто заполнитель, но он лежит в основе остальной части этого вопроса.

Перечислимый my_strategies содержит упорядоченный список стратегий для получения значения из удаленной службы; более предпочтительная стратегия запускается перед менее предпочтительной стратегией,

Иногда более предпочтительная стратегия терпит неудачу, и одни повторные попытки не исправят ситуацию. В этом случае стратегия вернет ноль.

Я вижу способ сделать это, полагаясь на каждый блок, таким образом:

result = nil

my_strategies.each do |strategy|
    result = strategy.get_result
    if not r.nil?
        break
    end
end

Это кажется излишне шумным. Мне интересно, есть ли метод, который я могу заменить some_method для моего первого примера; что-то похожее на .any?, но возвращающее значение, из-за которого блок становится истинным, а не просто возвращает истину.

Альтернативные подходы к тому, что я пытаюсь сделать, также приветствуются.

EDIT: я изначально задал этот вопрос, потому что пробовал этот блок кода:

result = my_strategies.find do |strategy|
    strategy.get_result
end

За исключением того, что это вернуло мне успешную стратегию, а не значение, которое она вернула, когда это произошло. Меня не волнует, какая стратегия принесла мне ценность, я просто хочу знать, в чем ценность.


person Ed Carrel    schedule 09.11.2011    source источник


Ответы (3)


Ваша потребность очень распространена, но, к сожалению, в ядре нет такой абстракции. Однако ребята из Facets давно обнаружили этот пробел и реализовали Enumerable#find_yield (он же Enumerable#map_detect):

result = my_strategies.map_detect { |strategy| strategy.get_result }

Или просто: result = my_strategies.map_detect(&:get_result). Ruby 2.0 реализует ленивые перечисления (для ранних версий используйте enumerable-lazy), так что теперь мы можем написать:

result = my_strategies.lazy.map(&:get_result).reject(&:nil?).first
person tokland    schedule 09.11.2011
comment
Да. Это своего рода шаблон, который я реализовал. Мне было интересно, есть ли где-то уже реализация этого; Я хочу избежать написания собственной версии существующей функциональности. - person Ed Carrel; 10.11.2011
comment
@Ed: нет, такой абстракции нет, я отредактирую ответ, чтобы добавить несколько ссылок. - person tokland; 10.11.2011

Вы можете использовать Enumerable#find для повторения массив, пока не получите результат:

result = nil

my_strategies.find do |strategy|
    result = strategy.get_result
end
person Platinum Azure    schedule 09.11.2011
comment
На самом деле, теперь, когда я наткнулся на Enumerable#find, стало намного лучше! - person Platinum Azure; 10.11.2011
comment
см. мою правку. Это дает мне нечто отличное от того, что я хочу вернуть, если только я не понимаю семантику find. - person Ed Carrel; 10.11.2011
comment
Я не уверен, но я думаю, что ОП хочет получить результат get_result, а не саму стратегию. - person tokland; 10.11.2011
comment
О стрелять. По какой-то причине я неправильно прочитал документы... Назначение в закрытии должно исправить это. - person Platinum Azure; 10.11.2011

Альтернативный подход к этому (без использования блока):

result = nil
num = 0

while result.nil?
  result = my_strategies[num].get_result
  num += 1
end
person robotcookies    schedule 09.11.2011