ブログ

読んで思い出す。忘れるために書く

始点終点を持つ範囲同士を比較するコードを考える

teratail.com

気になったので書いてみる

RSpec の shared examples に適用できそうだったから書いたというのはナイショ

仕様

質問文に書かれていた仕様は以下の通り

start:2:、 end: 6

start:6:、 end: 7

start:6:、 end: 9

はfalseです

なので、それに沿ったテストコードを書いていく

テストコードを書く

まずはテストを書く (今回は RSpec で書く)

テストをパスするコードはこんなふうに動いて欲しい

  • 始点と終点、2つの数値を保持できる
  • また、別の範囲と自身とを比較して、自身がその範囲に含まれているか否かを判定できる

「こういう実装になれば実際に使用する時に楽だなあ」みたいに理想像を考えて書く

shared_examples 'line cover?' do |start, _end|
  it "start:#{start}, end:#{_end}" do
    line2 = Line.new(start: start, end: _end)
    expect(line.cover?(line2)).to be_falsy
  end
end

describe 'Line' do
  context 'start:2, end:4' do
    let(:line) { Line.new(start: 2, end: 4) }

    context 'will got false result that compared by' do
      include_examples 'line cover?', 2, 6
      include_examples 'line cover?', 6, 7
      include_examples 'line cover?', 6, 9
    end
  end
end

上はリファクタリングをしたあとのテストコード

愚直にテストコードを書いてみると、テスト対象とする部分が、 startend の数値以外変わっていないことに気づく、ので書き換えた

(ここで一応、テストコードを書いたあとに、Ruby のコードとしてエラー無く動いてくれていること、テスト対象をまだ書いていないので全落ちすることを、テストの実行をして確認した)

実装を書く

Line =
  Struct.new(:start, :end, keyword_init: true) do
    def cover?(line)
      start <= line.start && self.end >= line.end
    end
  end

とくに複雑なことをする必要もなく、2つの数値を保持できればいいので Struct で定義した

比較に関しても、自身と同じ Struct をもらって比較できればいい

Ruby 2.5 からは keyword_init オプションが指定可能になったので利用している

結果

テスト結果がグリーンになったことが確認できた:

f:id:innocent-zero:20180212113402p:plain

Links