LoginSignup
3
3

More than 5 years have passed since last update.

ひとりTDDBC(LRUCache)仕様変更その1

Last updated at Posted at 2014-09-07

前回までのあらすじ

仕様変更その1

キャッシュのサイズを後から変更したいそうです。

cache = LRUCache.new(5)
cache.resize(2)

こんな感じでresizeメソッドに変更したいサイズを渡して
キャッシュのサイズを変更できるようにしたいと思います。

レビュー

仕様変更その1に対応したコード

テスト

RSpec.configureのprofile_examplesを無効にする

今回使っているRSpecのバージョンは3.1なのですが、
rspec --initで生成されたspec_helper.rb
=begin =endをアンコメントアウトして実行すると

[tddbc_lru_cache (feature/resize)]$ bundle exec rspec
Run options: include {:focus=>true}

All examples were filtered out; ignoring {:focus=>true}

...中略...

Finished in 0.00388 seconds (files took 0.15989 seconds to load)
10 examples, 0 failures

Top 10 slowest examples (0.00244 seconds, 62.9% of total time):
  LRUCache サイズを固定 :bが消える
    0.00115 seconds ./spec/lru_cache_spec.rb:34

 ...中略...

  LRUCache サイズを固定 何も消えない
    0.00008 seconds ./spec/lru_cache_spec.rb:4

Top 3 slowest example groups:
 ...中略...
    0.00015 seconds average (0.00046 seconds / 3 examples) ./spec/lru_cache_spec.rb:58

Randomized with seed 32109

こんな感じでテストをプロファイリングしてくれます。

テストスイートが大きくなり、最適化が必要になった時には便利ですが
今はまだ必要ないので、spec_helper.rb

#config.profile_examples = 10

この部分をコメントアウトしてプロファイリングを無効にしました。

仕様変更部分のテスト

RSpec.describe LRUCache, 'サイズを5から2に変更' do
  let(:cache) { LRUCache.new(5) }

  before do
    cache[:a] = 'alpha'
    cache[:b] = 'bravo'
    cache[:c] = 'charlie'
    cache[:d] = 'delta'
    cache[:e] = 'echo'
  end

  it ':a,:b,:cが消える' do
    cache.resize(2)
    expect(cache.to_hash).to match(
      d: 'delta',
      e: 'echo',
    )
  end

  it ':a,:b,:dが消える' do
    cache[:a]
    cache[:c]
    cache[:e]
    cache.resize(2)
    expect(cache.to_hash).to match(
      c: 'charlie',
      e: 'echo'
    )
  end

  it ':a,:b,:d,:cが消える' do
    cache[:a]
    cache[:c]
    cache[:e]
    cache.resize(2)
    cache[:f] = 'foxtrot'
    expect(cache.to_hash).to match(
      e: 'echo',
      f: 'foxtrot'
    )
  end
end
  • 何もせずにリサイズ
  • リサイズ前にデータを取り出してからリサイズ
  • リサイズ後にデータを入れる

この3つのテストケースで、リサイズしても正しくデータが消えているか(残っているか)をチェックします。

プロダクションコード

   def initialize(capacity)
     @capacity = capacity
     @container = {}
-    @stored_key_order = []
+    @contained_key_order = []
   end

@stored_key_order という名前は、データを入れた時のキーの順序という意図でしたが、
実際の役割は
次にキャッシュサイズを超えた時に消されるキーの優先順位
になっているので、それにふさわしい名前に変えたほうがよさそうです。

前回、こう書きましたが実装を進めるうちに@stored_key_order
役割が元に戻り@contianed_key_orderに変わりました。

def resize(new_capacity)
  delta = @capacity - new_capacity
  @capacity = new_capacity
  return if delta < 0
  shape_container(reduce_contained_keys(delta))
end

今回の仕様変更で追加したresizeメソッドはこんな感じになりました。

  • @capacityを新しいサイズにする
  • 新しいサイズの方が小さければ@containerをそのサイズに合わせる

@contained_key_orderふたたび

[][]=resizereduce_contained_keysの各メソッドで

  • 要素の削除
  • 要素の追加
  • 要素の入れ替え
  • 配列のサイズ変更

を直接Arrayのメソッドを使ってやっているのが気になります。

つづく

ひとりTDDBC(LRUCache)リファクタリング

3
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
3