nilとfalseを区別したい人生だった。あとPerlのような暗号を使うのをやめろ

Effective Rubyを読み始めたら面白かったので、ちょいちょいまとめていこうと思います。

Effective Ruby

Effective Ruby

昨夜は1章を読み、今日は2章を少し読みすすめつつ、1章の復習をしました。

1章のサブタイトルは、「Rubyに身体を慣らす」。他言語経験のあるRuby初心者にも役立つ(おそらくは)Rubyの常識的なことが書いてあります。

項目1 Rubyは何を真と考えているかを正確に理解しよう

nilとfalse以外はすべて真という話が説明される。0も真。

nilとfalseを区別しなければならない例として、設定情報を表すオブジェクトが挙げられている。設定としてのfalseなのか、未設定ゆえのnilなのかを区別したいケースは、たしかにありそう。

対処が2つ紹介される。

  • nil?メソッドで確認する
  • if false == foo で確認する

falseを左オペランドに置くのは、他のコードでオーバーライドされていない==メソッドが使われるようにするためとのこと。メソッドがどこで上書きされているか分からないような言語では、こういった配慮が重要なんだなーと感心する。

項目2 オブジェクトを扱うときにはnilかもしれないということを忘れないようにしよう

どんなオブジェクトでもnilになりうることを念頭において、防御的に書こうという話。

ここでも対処が2つ紹介される。

  • nil?メソッドで確認してから使う
  • nilでも上手く扱ってくれる型に変換してから使う

2つ目は、nillが来ても来なくてもいいような形に変えてしまおうというアプローチ。to_sメソッド、to_aメソッド、to_iメソッド、to_fメソッドを使って、それぞれ空白文字、要素なしの配列、0、0.0などの無害な値に変換してしまう。

配列にcompactメソッドを適用してから、空白でjoinするイディオム

配列要素がnilでもそうでなくても、よしなにやってくれる。なるほど。

irb(main):001:0> first="James"
=> "James"
irb(main):002:0> middle=nil
=> nil
irb(main):003:0> last="Hogan"
=> "Hogan"
irb(main):004:0> name = [first, middle, last].compact.join(" ")
=> "James Hogan"
irb(main):005:0> middle="Patrick"
=> "Patrick"
irb(main):006:0> name = [first, middle, last].compact.join(" ")
=> "James Patrick Hogan"

項目3 Rubyの暗号めいたPerl風機能を避けよう

暗号めいたというのは、記号の組み合わせで表現されるグローバル変数のこと。

例として挙げられていた、正規表現とマッチするための「=~」と、マッチしたグループを取り出す「$1, $2,...」は、先日の調整さん問題で使ったばかりだったので、ガーンという感じ。

さっそく修正。

修正前
def parse(input)
  if /^([01]?[0-2]\/[0-3]?\d)-([01]?[0-2]\/[0-3]?\d)\s([0-2]?\d):([0-5]?\d)-([0-2]?\d):([0-5]?\d)\s(\d)/ =~ input.join(" ") then
    return $1, $2, $3.to_i, $4.to_i, $5.to_i, $6.to_i, $7.to_f
  else
    puts "mm/dd-mm/dd H:M-H:M h形式で日付を入力"
    exit
  end
end
修正後
def parse(input)
  if m = input.join(" ").match(/^([01]?[0-2]\/[0-3]?\d)-([01]?[0-2]\/[0-3]?\d)\s([0-2]?\d):([0-5]?\d)-([0-2]?\d):([0-5]?\d)\s(\d)/) then
    return m[1], m[2], m[3].to_i, m[4].to_i, m[5].to_i, m[6].to_i, m[7].to_f
  else
    puts "mm/dd-mm/dd H:M-H:M h形式で日付を入力"
    exit
  end
end

ほかにも、$:の代わりに$LOAD_PATHを使えとか、$;や$/を避けてわかりやすい代替の変数を使えという話が出てくる。

ここでの戒めを一般化すると、「記号的なものを書きそうになったら、(特殊)グローバル変数を使おうとしていることを自覚して、わかりやすい代替の変数を探そう」になると思う。

今日はここまで。