対象読者
プログラミング初学者
リファクタリングとは
外部から見た時の挙動は変えずに、プログラムの内部構造を整理すること
構造の階層化やプログラム内の要素につける名前を簡潔なものに書き換えるなどがある
リファクタリングのメリット
- コードの煩雑さ(性能劣化)が低減するため、トラブルを起りにくくなる
- コードを理解しやすくなり、引き継ぎ時の手間が減る
- 修正、追加をより早く、正確に行えるため、トラブル時の対応が速くなる
リファクタリングのデメリット
チーム開発の場合、メンバーが同じ修正方法の認識を持っていないと、各々が考える方法での修正となり、結局ソースコードを全体でみるとバラバラのままになる
本題:ワンライナーへのリファクタリング
以下のコードに関して、リファクタリングを行っていきます。
array = [1, 2, 3, 4, 5].map do |el|
if el.odd?
el
end
end.compact!
前提知識
■ mapメソッド
オブジェクト.map { |変数|
# 実行したい処理
}
配列やハッシュオブジェクトの要素がひとつずつ順に配列内から取り出され、変数に要素が代入され、指定した変数名をブロック内で使用することができる。また、ブロック引数の値を新たな配列(= 既存の配列の書き換え(mutate)をしない)にして返す。
■ ブロック
do ~ end で囲まれた引数
■ odd?メソッド
奇数かどうかをtrue or falseで判定するメソッド
偶数の場合は、even?メソッドがある
2.odd? # => false
3.odd? # => true
2.even? # => true
3.even? # => false
■ compact!メソッド
自身から破壊的に nil を取り除き、変更が行われた場合は self を、そうでなければ nil を返す
また、compact メソッドは自身から nil を取り除いた配列を生成して返す
## arrayがnilを含んでいる時
array = [1,2,3,4,5,nil]
# compact
array.compact
=> [1, 2, 3, 4, 5]
# compact!
array.compact!
=> [1, 2, 3, 4, 5]
## arrayにnilがない時
array = [1,2,3,4,5]
# compact
array.compact
=> [1, 2, 3, 4, 5]
# compact!
array.compact!
=> nil
以上を踏まえたリファクタリング結果
#1
array = [1, 2, 3, 4, 5].map { |el| el if el.odd? }.compact!
#2
array = (1..5).to_a.delete_if { |el| el.even? }
#3
array = (1..5).to_a.delete_if(&:even?)
#4
array = [1, 2, 3, 4, 5].select{ |el| el.odd?}
上記以外のメソッド
■ delete_ifメソッド
要素を順番にブロックに渡して評価し、その結果が真になった要素をすべて削除する
delete_if は常に self を返す
array = [0, 1, 2, 3, 4, 5]
array.delete_if{|x| x % 2 == 0}
p array #=> [1, 3, 5]
■ to_aメソッド
ハッシュ、範囲オブジェクトなどを配列に変換するメソッド
(1, 2, 3, 4, 5).to_a # => [1, 2, 3, 4, 5]
mapメソッドを使用した場合は自動で配列を作成してくれていたので、to_aメソッドは不要でした
"a"はarray(配列)なので、"to_a"で"配列に(変換する)"メソッドですね
■ 範囲演算子
p (1..5).to_a # => [1, 2, 3, 4, 5]
p (1...5).to_a # => [1, 2, 3, 4]
■ array.method(&:method)
# array.method(&:メソッド名)の記法
['a','b'].map(&:upcase) #=> ["A", "B"]
■ selectメソッド
各要素に対してブロックを評価した値が真であった要素を全て含む配列を返す
真になる要素がひとつもなかった場合は空の配列を返す
ブロック({})を省略した場合は、各要素に対しブロックを評価し真になった値の配列を返すような Enumerator を返す
[1,2,3,4,5].select # => #<Enumerator: [1, 2, 3, 4, 5]:select>
[1,2,3,4,5].select { |num| num.even? } # => [2, 4]