give IT a try

プログラミング、リモートワーク、田舎暮らし、音楽、etc.

「プロを目指す人のためのRuby入門」で6.3.3項のサンプルコードがなぜか動いてしまう場合の対処法

このエントリは拙著「プロを目指す人のためのRuby入門(通称・チェリー本)」のサポート記事です。

6.3.3項(187〜188ページ)に載っているサンプルコードをirb上で写経していくと、書籍ではエラーになるはずのコードがエラーにならないケースがあります。

f:id:JunichiIto:20200906144357p:plain

これはirb上で連続してコードを打ち込んでいるためです。
一度irbを再起動させてからエラーになるコードを入力すると、書籍の説明通りエラー(NameError)が発生します。

f:id:JunichiIto:20200906144614p:plain

もっとくわしく

6.3.3項のサンプルコードをirb上で打ち込むと、次のコードを実行することになります。

text = '私の誕生日は1977年7月17日です。'
# キャプチャの名前がそのままローカル変数に割り当てられる
if /(?<year>\d+)(?<month>\d+)(?<day>\d+)/ =~ text
  puts "#{year}/#{month}/#{day}" #=> 1977/7/17
end

この時点でyearmonthdayという3つのローカル変数がirb内に定義されます。

その次に出てくる以下のサンプルコードは、「正規表現を使ってもローカル変数が作成されない」ということを説明するためのコードです。

text = '私の誕生日は1977年7月17日です。'
# 正規表現が右辺に来るとローカル変数が作成されない
if text =~ /(?<year>\d+)(?<month>\d+)(?<day>\d+)/
  puts "#{year}/#{month}/#{day}"
end
#=> NameError: undefined local variable or method `year' for main:Object

なのですが、1つ前のコードを実行したことにより、すでにyearmonthdayがローカル変数として定義されてしまっています。
そのため、本来であれば「yearが未定義なのでエラー(NameError)」となるべきところが、エラーにならず普通に実行できてしまうわけです。

# 本当はyearもmonthもdayもないよ!となるはずが、すでに存在するので動いてしまう
puts "#{year}/#{month}/#{day}"

irbを再起動すればローカル変数はすべてクリアされます。
ですので、再起動した直後に先ほどのコードをもう一度実行すれば、期待どおり「yearが未定義なのでエラー(NameError)」になります。

まとめ

この件はフィヨルドブートキャンプの生徒さんやSNS上の読者さんで、同じ疑問を持っていた人を見かけたので、今回ブログ記事として説明させてもらいました。
記述ミスではないとはいえ、初心者さんにとってはたしかにややこしい落とし穴になってしまっているため、改訂版を出す機会があればこの点をひとこと補足しておこうと思います。