Akata Works

東京エンジニア。主にRuby,Go,たまにAWSとiOS。ゲーム音楽が好きです。連絡はTwitterかakata.onen@gmail.comまで

Rubyのスコープと変数定義で「What!?」ってなった話

最近になって急に、開発言語がPerlからRubyObjective-Cに早変わりして大変です。

そして、Perlの感覚でRubyを書いていると「What!?」ってなったことがあったので、
久しぶりに記事にしてみました。

下記はPerlのコードです。

#!/usr/bin/env perl

use strict;
use warnings;

use utf8;

if (0) {
  my $message = 'hoge';
}
print "$message\n";

が、当然エラーは出ます。

Global symbol "$message" requires explicit package name at..
Execution of.. aborted due to compilation errors.

message変数のスコープがIfブロック内なので当たり前ですよね。


下記はRubyのコードです。

#!/usr/bin/env ruby

if false
  message = 'hoge'
end

puts "#{message}"

が、エラーは出ません

(何も表示されない)

どうやら、RubyにおいてIfやFor,Whileブロックはスコープを生成しないらしいです。


そのため、PerlRubyでは以下の様な違いが出ます。

Ruby

#!/usr/bin/env ruby

hoge = 10
puts hoge

if true
  hoge = 100
  puts hoge
end

puts hoge
10
100
100

Perl

#!/usr/bin/env perl

use strict;
use warnings;

use utf8;

my $hoge = 10;
print "$hoge\n";

if (1) {
  my $hoge = 100;
  print "$hoge\n";
}

print "$hoge\n";
10
100
10

それでは、話を下記のコードに戻します。

#!/usr/bin/env ruby

if false
  message = 'hoge'
end

puts "#{message}"

さて、Ifブロックがスコープを生成しないため、Ifブロック外で変数が見えていることは分かりましたが、
何故、undefined local variable or methodエラーが出ない理由が分かりません。

実際にmessage変数のクラスを表示するとNilClassになっています。

#!/usr/bin/env ruby

if false
  message = 'hoge'
end

puts "#{message.class}";
NilClass

理由は簡単で、ちゃんと定義されているからです。

どうも、Rubyのパーサは代入を見つけた時点で、勝手に変数を定義するようになっているそうです。

知っていれば当たり前なのかも知れませんが、
慣れていない自分からすると、
初期化されていない気がして不安になってしまいます(||゚Д゚)ヒエー!!

参考URL