Вот пример того, как создать альтернативу для Enumerable#zip
, которая работает лениво и не создает целый массив.Он объединяет мою реализацию из Closure interleave
и два других ответа здесь (с использованием значения sentinel для обозначения конца Enumerable
достигнута - факт, вызывающий проблему, заключается в том, что next
перематывает Enumerable
как только он достиг конца).
Это решение поддерживает несколько параметров, поэтому вы можете сравнить n структур сразу.
module Enumerable
# this should be just a unique sentinel value (any ideas for more elegant solution?)
END_REACHED = Object.new
def lazy_zip *others
sources = ([self] + others).map(&:to_enum)
Enumerator.new do |yielder|
loop do
sources, values = sources.map{|s|
[s, s.next] rescue [nil, END_REACHED]
}.transpose
raise StopIteration if values.all?{|v| v == END_REACHED}
yielder.yield values.map{|v| v == END_REACHED ? nil : v}
end
end
end
end
Итак, когда у вас есть вариантиз zip
, который работает лениво и не останавливает итерацию, когда первый перечисляемый достигает конца, вы можете использовать all?
или any?
, чтобы фактически проверить соответствующие элементы на равенство.
# zip would fail here, as it would return just [[1,1],[2,2],[3,3]]:
p [1,2,3].lazy_zip([1,2,3,4]).all?{|l,r| l == r}
#=> false
# this is ok
p [1,2,3,4].lazy_zip([1,2,3,4]).all?{|l,r| l == r}
#=> true
# comparing more than two input streams:
p [1,2,3,4].lazy_zip([1,2,3,4],[1,2,3]).all?{|vals|
# check for equality by checking length of the uniqued array
vals.uniq.length == 1
}
#=> false