はじめに
Effective Rubyを私が読んで勉強になったことを書いてます。
本書を読んだのは2回目ですが、1回目は何がどう役立つのか全くわかりませんでした。
2回目の今回もいまだに「これって一体どういった場面で効果的なの?」、「そもそも何いってんのこれ?」って部分は多数というかそっちの方が多い気もしますが、以前より「なるほど!」と思った部分があったので、「なんかコーディング力上がってない気がする」って悶々としている人は読んでみると発見があるかもしれません。
本全体の内容を紹介するものではなく
RoRを書いていて、「あ〜こう書けば良かったんだ!」と自分のアンテナに引っかかったところだけを書いていくので、本全体の内容を知りたい方は以下の記事など参照すると良いと思います。
https://qiita.com/fujigaki/items/9d64fa49efcbe7017d8c
勉強になったこと
項目2 オブジェクトを扱うときにはnilかもしれないことを忘れないようにしよう
undefined method 'XXXX' for nil:NilClass(NoMethodError)
以前は、RoRしか経験がなく特に気になってませんでしたが、Typescriptではじめて静的型付き言語を触ってから嫌いになったエラーの1つです。
上記のエラーが意図せず発生しないように、nilになる可能性をできる限り排除しましょうってことが大事で、すぐにできることとして
[4] pry(main)> nil.to_a
=> []
[5] pry(main)> nil.to_s
=> ""
[6] pry(main)> nil.to_i
=> 0
があげられます。
このように型付けがないのをカバーするためにto_aやto_sに代表される変換メソッドを入れておくことでnilが排除されるので、よりエラーが発生しにくいコードになるってことですね。
項目8 サブクラスを初期化するときはsuperを呼び出そう
[7] pry(main)> class Parent
[7] pry(main)* attr_accessor(:name)
[7] pry(main)* def initialize
[7] pry(main)* @name = "サンプル太郎"
[7] pry(main)* end
[7] pry(main)* end
=> :initialize
[8] pry(main)>
[9] pry(main)> class Child < Parent
[9] pry(main)* attr_accessor(:age)
[9] pry(main)* def initialize
[9] pry(main)* @age = 14
[9] pry(main)* end
[9] pry(main)* end
=> :initialize
[10] pry(main)>
[11] pry(main)> child = Child.new
=> #<Child:0x00007fcf69ed6a08 @age=14>
[12] pry(main)> child.name
=> nil
上記のようにChildクラスにParentを継承していた時にChildをnewしてもParentのnameにはアクセスできないんですよね。
最近、これと全く同じことをしてなんでアクセスできない?って思って調べた結果superが必要だったと思い出しました。
「あーこれこの間ハマったやつや!」って思ったので改めて。
[13] pry(main)> class Child < Parent
[13] pry(main)* attr_accessor(:age)
[13] pry(main)* def initialize
[13] pry(main)* super()
[13] pry(main)* @age = 14
[13] pry(main)* end
[13] pry(main)* end
=> :initialize
[14] pry(main)> Child.new
=> #<Child:0x00007fcf5cfda718 @age=14, @name="サンプル太郎">
項目11 構造化データの表現にはHashではなくStructを使おう
Typescriptを触っていて例えばUserの配列を作ろうとしたとき
type User = {
id: string
name: string
address: string
}
const users: User[] = []
Userって型を作ってその配列だよ!って型宣言してたんですが、これと似たようなことだと認識してます。
[2] pry(main)> sample_users = [
[2] pry(main)* { id: "1", name: "太郎", age: 8, nickname: "とっとこ太郎" },
[2] pry(main)* {id: "2", name: "花子", age: 11, nickname: "トイレの花子"}
[2] pry(main)* ]
=> [{:id=>"1", :name=>"太郎", :age=>8, :nickname=>"とっとこ太郎"}, {:id=>"2", :name=>"花子", :age=>11, :nickname=>"トイレの花子"}]
[3] pry(main)>
[4] pry(main)> array = []
=> []
[5] pry(main)>
[6] pry(main)> sample_users.each do |user|
[6] pry(main)* array << { name: user[:name], nickname: user[:nickname] }
[6] pry(main)* end
=> [{:id=>"1", :name=>"太郎", :age=>8, :nickname=>"とっとこ太郎"}, {:id=>"2", :name=>"花子", :age=>11, :nickname=>"トイレの花子"}]
[7] pry(main)>
[8] pry(main)> array[0][:missing]
=> nil
見て分かる通り、missingなんてkeyがないので、本来ならエラー発生してほしいんですが、nilが返ってきてしまいます。
これだと仮にタイポしてたとしても気づかないってことが。
それを防ぐために、Structで型を定義してそれをarrayに詰めようって話ですね。
[19] pry(main)> SampleUser = Struct.new(:name, :nickname)
=> SampleUser
[23] pry(main)> array = []
=> []
[24] pry(main)> sample_users.each do |user|
[24] pry(main)* array << SampleUser.new(user[:name], user[:nickname])
[24] pry(main)* end
=> [{:id=>"1", :name=>"太郎", :age=>8, :nickname=>"とっとこ太郎"}, {:id=>"2", :name=>"花子", :age=>11, :nickname=>"トイレの花子"}]
[26] pry(main)> array
=> [#<struct Sample name="太郎", nickname="とっとこ太郎">, #<struct Sample name="花子", nickname="トイレの花子">]
[28] pry(main)> array[0]
=> #<struct Sample name="太郎", nickname="とっとこ太郎">
[29] pry(main)> array[0][:missing]
NameError: no member 'missing' in struct
[30] pry(main)> array[0][:name]
=> "太郎"
[31] pry(main)> array[0].name
=> "太郎"
[32] pry(main)> array[0].missing
NoMethodError: undefined method `missing' for #<struct Sample name="太郎", nickname="とっとこ太郎">
from (pry):36:in `<main>'
なるほど、nilが返ってこなくなりエラーが発生!これはいいですね!
項目19 reduceを使ってコレクションを畳み込む方法を身に付けよう
users = [{ name: "太郎", age: 22 }, { name: "花子", age: 11 }, { name: "大吉", age: 55 }]
ここから21才以上ののname取り出したい時って
[45] pry(main)> users.select { |u| u[:age] >= 21}.map{|u| u[:name]}
=> ["太郎", "大吉"]
って書くと思うのですが、これってselectで21才以上の新しい配列を作ってさらにそこにnameだけを抽出した新しい配列をつくるという無駄な処理があります。
ここでreduce使うと
[48] pry(main)> users.reduce([]) do |names, user|
[48] pry(main)* names << user[:name] if user[:age] >= 21
[48] pry(main)* names
[48] pry(main)* end
=> ["太郎", "大吉"]
[49] pry(main)>
うん。シンプル。注意としてはアキュムレーターををしっかりとブロックの中で返してあげることですね。
感想
2回目読んでみましたが、リファクタリングに使えそうなTipsがあって読んで良かったと思います。
正直、第5章メタプログラミングと第8章メモリ管理とパフォーマンスは、どこで使うのか?、そもそも何いってるかようわからんって部分が大半だったので次本書を手にするときは「なるほど!」って思いたいところです