2016-07-13 2 views
0

(Я уже написал решение проблемы ниже, но хотел бы спросить, может ли кто-нибудь предложить более чистое решение, более того, находясь в «Духе» Ruby).Ruby: Поиск записи на основе предиката - предложите более приятные решения

ПРОБЛЕМА: У меня есть (упорядоченный) набор игл, (упорядоченный) набор элементов в стоге сена и предикат, который принимает игла и элемент стога сена и дает истину или ложь. Задача состоит в том, чтобы найти первую иглу, где есть элемент haystack, где предикат является истинным, а затем возвращает элемент haystack. Мы не заинтересованы в игле. Иглы и стог сена представлены в виде массива.

Например, если

needles = [17,3,7,121] 
haystack = [40,30,70] 

и предикат

def p(needle, hay) 
    hay % needle == 0 
end 

результат должен быть 30, потому что для иглы 17, ни один элемент в стогу не выполняет предикат, но для иглы 3, второй элемент (30).

Вот моя реализация:

found = nil 
needles.find do |n| 
    found = haystack.find { |he| p(n,he) } 
    break if found 
end 

Что я нахожу немного неестественным, что я в основном выбросить результат внешней find и нужно носить с переменной found, который в конечном счете имеет место результата. Я искал более кратким выражением, которое непосредственно присваивает результат found, т.е .:

found = ...... 

Любые идеи?

+1

Для дальнейшего использования вопросы, подобные этому, лучше размещены на http://codereview.stackexchange.com/ - поскольку у вас уже есть решение проблемы, но вы ищете мнения о качестве кода. –

ответ

1

Важно отметить, что это элемент haystack, который вы хотите найти, а не needle. Следовательно, эта переменная должна быть той, которая находится в вашем внешнем цикле.

Было бы затем более идиоматическим использовать Enumerable#any? для проверки на наличие игл, которые удовлетворяют предикат, так как вы на самом деле только заботитесь о возвращаемом значении true/false.

Конечный результат:

haystack.find do |he| 
    needles.any? { |needle| p(needle, he) } 
end 

EDIT: я немного неправильно понял вопрос, извините. Если мы заказ иглы, и найти первый стог сена, который удовлетворяет предикату «высшей упорядоченное» иглы, то мы могли бы сделать:

haystack 
    .select { |he| needles.any? { |needle| p(needle, he) } } 
    .min_by { |he| needles.index { |needle| p(needle, he) } } 

Или в качестве альтернативы (более эффективного) решения, как о :

haystack.min_by do |he| 
    needles.index { |needle| p(needle, he) } || Float::INFINITY 
end 

Это забавное использование Float::INFINITY используется в случае, если нет needles не соответствует условию для haystack элемента, в этом случае метод возвращает indexnil.

+0

К сожалению, это дало бы неправильный результат.Представьте себе, что первый элемент стога сена будет соответствовать третьей иголке, тогда я бы получил его в результате, даже если, скажем, вторая игла соответствовала бы последнему элементу сена. Ваше решение находит первый элемент стопки сена, соответствующий ** некоторой ** игле, т. Е. Все иглы обрабатываются равными. – user1934428

+1

Ах, извините, я пропустил этот момент. Это было бы яснее для меня, если бы вы предоставили более полный пример, показывающий этот сценарий. Я обновил свой ответ двумя возможными решениями. –

+0

Действительно отличные решения, и я теперь узнал о 'min_by'. Если мы пойдем на эффективность, я должен, как вы указали, использовать последнюю версию ('haystack.min_by ...'), ** и ** не пропустить весь массив '' игл'''', до (но не включая) иглу с наименьшим индексом, для которой уже был найден элемент стога сена. К сожалению, эта последняя оптимизация делает код более неуклюжим. – user1934428