前回までのあらすじ
- ひとりTDDBCをやります
- お題はt_wadaさんのTDDBCお題スライド P.5
- Githubのこのリポジトリで進めます
- Vol.1
仕様変更その1
キャッシュのサイズを後から変更したいそうです。
cache = LRUCache.new(5)
cache.resize(2)
こんな感じでresize
メソッドに変更したいサイズを渡して
キャッシュのサイズを変更できるようにしたいと思います。
レビュー
テスト
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ふたたび
[]
、[]=
、resize
、reduce_contained_keys
の各メソッドで
- 要素の削除
- 要素の追加
- 要素の入れ替え
- 配列のサイズ変更
を直接Array
のメソッドを使ってやっているのが気になります。