Rails で定義されている Range#overlaps?
の定義は
def overlaps?(other)
cover?(other.first) || other.cover?(first)
end
となっている。other
と対比しやすいよう self
を補うと
self.cover?(other.first) || other.cover?(self.first)
となる。
ところで、区間が重なっているか調べるには
self.first <= other.end && other.first <= self.end
という別式も知られている。これは、区間どうしが重ならない条件
self.end < other.first || other.end < self.first
の否定として得られる。
Rails の Range#overlaps?
の定義が、この別式と等しいかどうか見てみよう。区間は両端を含み、first <= end
となっているとする。
さっき self
を補った式
self.cover?(other.first) || other.cover?(self.first)
からスタートする。cover?
の定義を展開する。
(self.first <= other.first && other.first <= self.end) ||
(other.first <= self.first && self.first <= other.end)
&&
を ||
した形になった。ここで
(A && B) || (C && D)
== (A || (C && D)) && (B || (C && D))
== (A || C) && (A || D) && (B || C) && (B || D)
だから、当てはめて
(self.first <= other.first || other.first <= self.end) &&
(self.first <= other.first || self.first <= other.end) &&
(other.first <= self.first || other.first <= self.end) &&
(other.first <= self.first || self.first <= other.end)
を得る。第二項と第三項は冗長、たとえば第二項で self.first <= other.first
のときは常に self.first <= other.end
を満たすので、まとめて
(self.first <= other.first || other.first <= self.end) &&
(self.first <= other.end) &&
(other.first <= self.end) &&
(other.first <= self.first || self.first <= other.end)
となる。ここで第一項の
(self.first <= other.first || other.first <= self.end)
に注目しよう。実は簡単な条件じゃないかという匂いがするので二重否定をつけて
!!(self.first <= other.first || other.first <= self.end)
まずド・モルガンと不等号を反転して
!(self.first > other.first && other.first > self.end)
だが、self.first <= self.end
なので !(...)
の中身は常に成り立たない。つまり
!false
なので、第一項は true
だった。同様に第四項も true
になって
true &&
(self.first <= other.end) &&
(other.first <= self.end) &&
true
であり、true &&
を縮約して別式と同じ
self.first <= other.end && other.first <= self.end
が得られた。