こりゃあ面白い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?
の罠でした!
え!!括弧を使わずにプログラムを!?
できらぁっ!