Rubyでは後置ifというif文をワンライナーで書くことができるものが存在します。
hoge = “hogehoge” If false
みたいなやつですね。
後置ifはすごい便利で、短いときには全部後置ifにしてやるざヒャッハーとなるけど、ちょっと待ってほしい。
クイズタイム
一旦、以下のコードで何が出力される考えてみよう。
class Person attr_accessor :name def say_name name = "三郎" if name.nil? p name end end taro = Person.new taro.name = "太郎" taro.say_name
これは何が出力されるだろうか。 実行してみると,「太郎」ではなく「三郎」が出力されます。
では、これはどうだろう。
class Person attr_accessor :name def say_name if name.nil? name = "三郎" end p name end end taro = Person.new taro.name = "太郎" taro.say_name
後置ifをブロックにしただけですが、出力は変わります。今度は「三郎」ではなく「nil」です。
こんな風に場合によっては、書き方によって挙動が変わってくる。
違いの要因は処理の順番
taro.name = "太郎"
としているのに、なぜ三郎が出るんだ、nilって何だと思ったかもしれません。
順番に見ていきましょう。
class Person attr_accessor :name def say_name name = "三郎" if name.nil? p name end end taro = Person.new taro.name = "太郎" taro.say_name
まず最初のこのコードですが、say_name
が呼ばれるとname = "三郎" if name.nil?
が評価されます。このname
が何を指しているかがポイントですが、ローカル変数を最初に探して、その後にメソッドなりを探しにいきます。
if name.nil?
でtrue
と判断されるけど、このnameはself.name
を指しているわけじゃなくて、ローカル変数のname = “三郎”
を指している。でも評価した段階では、その変数はnil。プログラムの読み込み順序と自分たちの読み込み順序が異なっているのがわかる。
1. nameは何を指している?→nameというローカル変数があるのでそれ 2. 指しているのはname=“三郎” 3. だけど、`if name.nil?`で評価した段階ではそのnameはnil
結果として、条件はtrueとなり、三郎が出力される。
class Person attr_accessor :name def say_name if name.nil? name = "三郎" end p name end end taro = Person.new taro.name = "太郎" taro.say_name
これは実行するとnilが出力される。if name.nil?
で指しているnameはself.name
なので、if文の中は実行されずにnilが返る。ifの中身は関係ない。
まあこんな現象が起きるのは、変数名とattr_accesserで指定している名称が同じ場合ぐらいでそうそう起きないと思う。
最初のコードも以下のように変数名を変えて書けば、直感的にわかるしね。
class Person attr_accessor :name def say_name aaa = "三郎" if name.nil? p aaa end end taro = Person.new taro.name = "太郎" taro.say_name
まあそもそも、attr_accessor
やインスタンス変数をちゃんと使ったりすれば、そんなことは起きないだろう。
class Person attr_accessor :name end taro = Person.new taro.name = "太郎" p taro.name #=>"太郎"
class Person attr_accessor :name def say_name @name = "三郎" if @name.nil? p @name #=>"三郎" end end taro = Person.new taro.say_name