(Ruby) Как вы проверяете, содержит ли диапазон подмножество другого диапазона?
Вопрос
Если у меня есть два диапазона, которые перекрываются:
x = 1..10
y = 5..15
Когда я говорю:
puts x.include? y
результатом является:
false
потому что эти два диапазона перекрываются лишь частично.
Но если я хочу, чтобы это было "true" при частичном перекрытии двух диапазонов, как бы я это написал?Другими словами, мне нужен способ узнать, когда один диапазон содержит подмножество другого диапазона.Я предполагаю, что есть элегантный способ написать это на Ruby, но единственные решения, которые я могу придумать, являются подробными.
Решение
Будьте осторожны, используя это с большими диапазонами, но это элегантный способ сделать это:
(x.to_a & y.to_a).empty?
Другие советы
Эффективный способ - сравнить пределы
(x.first <= y.last) and (y.first <= x.last)
Вы также могли бы преобразовать диапазоны в Наборы, поскольку вы в основном выполняете здесь пересечение множеств.Может быть проще, если вы имеете дело с более чем двумя диапазонами.
x = (1..10).to_set
y = (5..15).to_set
!(x & y).empty? #returns true (true == overlap, false == no overlap)
Этот метод может быть использован для эффективной проверки перекрытия между несколькими диапазонами:
def range_overlap?(ranges)
sorted_ranges = ranges.sort
sorted_ranges.each_cons(2).each do |r1, r2|
return true if r2.first <= r1.last
end
return false
end
def test(r)
puts r.inspect, range_overlap?(r)
puts '================'
r = r.reverse
puts r.inspect, range_overlap?(r)
puts '================'
end
test [[1,9], [10, 33]]
test [[1,10], [5, 8]]
test [[1,10], [10, 33]]
Если диапазон включает либо начало, либо конец второго диапазона, то они перекрываются.
(x === y.first) or (x === y.last)
это то же самое, что и это:
x.include?(y.first) or x.include?(y.last)
Но если я хочу, чтобы это было "true", когда есть частичное перекрытие между двумя диапазонами, как бы я это написал?
Вы можете преобразовать диапазоны в массив и использовать &
оператор (соединение).Это возвращает новый массив со всеми элементами, встречающимися в обоих массивах.Если результирующий массив не пуст, это означает, что есть несколько перекрывающихся элементов:
def overlap?(range_1, range_2)
!(range_1.to_a & range_2.to_a).empty?
end
Если вы проверяете на перекрытие, то я бы просто сделал
(x.include? y.first) or (x.include? y.last)
поскольку один диапазон должен будет включать по крайней мере один из концов другого.Для меня это более интуитивно понятно, чем общепринятый ответ conjuction, хотя и не совсем так эффективно, как предельное сравнение MarkusQ.
Рельсы имеют Диапазон # перекрывается?
def overlaps?(other)
cover?(other.first) || other.cover?(first)
end
Если вы используете Ruby 2.6, вы можете использовать Range#cover?
с другим Range
.
(1..5).cover?(2..3) #=> true
(1..5).cover?(0..6) #=> false
(1..5).cover?(1...6) #=> true
Некоторые полезные перечислимые методы:
# x is a 'subset' of y
x.all?{|n| y.include? n}
# x and y overlap
x.any?{|n| y.include? n}
# x and y do not overlap
x.none?{|n| y.include? n}
# x and y overlap one time
x.one?{|n| y.include? n}