こりゃあ面白いRubyistだぜ
こりゃあどうしても括弧を使わずにプログラムを書いてもらおう。
タイトルは煽りです。括弧を使わずに書くことに慣れすぎて思わぬところで意図しない結果になってしまうというdefined?の注意点を書きます。
if defined? foo && foo.is_a? Array
p 'true'
else
p 'false'
end
さて、これを実行するとどうなるか・・・?
fooが定義されてない時点でdefined?がnilを返すから"false"が出力されるに決まってらぁ!
そうはいかないぜ食いしんぼうさん。これはsyntax errorになります。なぜかは後で説明しますがsyntax errorを解消するにはis_a?の引数を括弧で囲ってあげます。
if defined? foo && foo.is_a?(Array)
p 'true'
else
p 'false'
end
さて、これを実行するとどうなるか。
fooが定義されてない時点でdefined?がnilを返すから"false"が出力されるに決まってらぁ!
いいえ、違います。
実はこれ"true"が出力されます。
defined?についておさらいすると、defined?は定義されていない式を与えるとnilを返します。定義されている場合は評価した式の種別を文字列で返します。trueではありません文字列です。文字列がtrueとして評価されるだけです。
例えばdefined? fooを実行するとfooが定義されていなければnil, fooが定義されていれば"local-variable"を返します。
さてなぜこのような結果になるかというとdefined? foo && foo.is_a?(Array)は括弧がないためfooではなく**foo && foo.is_a?(Array)**を評価していることになるからです。
つまりfooという変数が定義されているかではなく**foo && foo.is_a?(Array)**という式が定義されているか意味になり、これはfooが定義されているかどうかに関わらずnilでなく"expression"を返します。
※式が定義されているかという言葉を使いましたがdefined?にどんな式を与えても"expression"を返すようです。
これでなぜif defined? foo && foo.is_a? Arrayがsyntax errorになるかわかりましたね。**foo && foo.is_a? Array**という式は正しいRubyの書き方ではないからです。
ですのでこのように括弧を使えば意図したとおりの結果になります。
if defined?(foo) && foo.is_a?(Array)
p 'true'
else
p 'false'
end
ついでなのでもう一点罠を
if false
foo = []
end
if defined? foo
p 'true'
else
p 'false'
end
さて、これを実行するとどうなるか。
ifがfalseに固定されていてfooに配列が代入されることはあり得ないから、定義されていない変数fooをdefined?で評価するとnilを返し"false"が出力されるに決まってらぁ!
違います。実はこれも"true"が出力されます。fooへの代入(ローカル変数fooの宣言)があると例えifで分岐されず[]が代入されなくてもfooは宣言済みとしてnilが入るからです。
以上、知らないとハマるdefined?の罠でした!
え!!括弧を使わずにプログラムを!?
できらぁっ!