Rubyの変数のお勉強

さてさて、三日坊主どころか始めてすらいない状態が続いてましたね。
そろそろやれよと。勉強しますと自分で言い出したんだからやりましょう。

と、言うわけで勉強第一回としてRubyの変数について勉強します。

と、言いたいところだけどまず「ruby」なのか「Ruby」なのかどっちで書くのが良いのだろう?

Perlの場合は「Perl」が一般的だ。公式HPでは「Ruby」になってるのでとりあえず「Ruby」でいきます。

さぁ、勉強開始といきますか。

新しい言語を勉強するのって楽しいですね。

基本的なスタンスとして「Perlで言うとこういうこと」みたいな僕的解釈を入れつつ勉強してみようと思ってます。

よし!

そろそろやるぞ!

まずは変数の勉強でもするか!

さぁ、Rubyにはどんな変数があるかな?

screenshotプログラミング言語 Ruby リファレンスマニュアル

とりあえずこれだけあるらしい。

よし!

一個ずつ行くぞ!

  • ローカル変数

小文字の英数字か'_'で始まる識別子はローカル変数またはメソッド呼び出しですとある。
また、ローカル変数のスコープは宣言されたいからブロックが終了するまで、もしくはファイルスコープ。

Perlのmy宣言のスコープとほぼ同一ですね。

またドキュメントに、

 v = 1 if false # 代入は行われないが宣言は有効
 p defined?(v)  # => "local-variable"
 p v            # => nil

という例もあって、ああPerlでも同じことになるよねーとか。

 my $v = 1 if 0;
 print $v;       # undef

と、ここまで調べてみてひとつ気になった点がひとつ。
あぁ、Rubyにもstrict的なものは無いんだなぁと。
宣言されていない変数のコンパイル時チェックはできないみたいですな。まぁ、仕方ない。

オブジェクトのインスタンス変数。
PHPみたいにvarで宣言したりはしないのかな?ま、それはクラスの勉強にて。
'@'で始まる変数がインスタンス変数になるみたい。
Perl使いの僕としては「うお、配列ぢゃないのか!?」と思ってしまう。

とりあずPerlではblessとハッシュを使ってインスタンス変数を実現します。

 # Perl
 package Foo;
 sub new {
     my $class = shift;
     my $bar   = shift;
     bless { bar => $bar } , ref $class || $class;
 }
 sub get {
     my $self = shift;
     $self->{bar};
 }
 
 # Ruby
 class Foo
   def initialize (bar)
     @bar = bar
   end
   def get ()
     @bar
   end
 end

こんな感じかな。
あと、ドキュメントには「インスタンス変数はそのクラスまたはサブクラスのメソッドから参照できます。」って書いてたんでどうやらインスタンス変数は強制privateになるっぽいですね。
んー良い。

  • クラス変数

'@@'で始まる変数がクラス変数ということ。

クラス内で使用できる変数、継承されても共有される変数ということで、
「お、もしかしてClass::Data::Inheritableみたいなことができるの??」
と思って試してみたんですが、ちょっと違うようだ。

 class Foo
   @@foo = 1
   def get()
     return @@foo
   end
 end
 class Bar < Foo
   @@foo += 1
   def get()
     return @@foo
   end
 end
 class Baz < Bar
   @@foo += 1
   def get()
     return @@foo
   end
 end
 
 p Foo.new.get # 3
 p Bar.new.get # 3
 p Baz.new.get # 3

全部「3」になったorz。
継承されても共有されるってことは派生クラスで値を変更したら基底クラスにも当然影響があるわけですな。
ちなみにPerlのClass::Data::Inheritableは同じようにクラス変数を提供するが、継承した場合に派生クラスで値を書き換えても基底クラスには影響をあたえないのでちょっと動作が変わってきます。

 package Foo;
 use strict;
 use base qw/Class::Data::Inheritable/;
 __PACKAGE__->mk_classdata('foo',1);
 
 package Bar;
 use base qw/Foo/;
 __PACKAGE__->foo(__PACKAGE__->foo+1);
 
 package Baz;
 use base qw/Bar/;
 __PACKAGE__->foo(__PACKAGE__->foo+1);
 
 local $\ = "\n"; 
 print Foo->foo; # 1
 print Bar->foo; # 2
 print Baz->foo; # 3

あと、クラス変数はクラスの外からは参照できないみたい。

'$'で始まる変数は何処からでもアクセスできるグローバル変数
Perlで例えると・・・・。
うーん、Perlで例えるのは難しいな。
Perlの変数という奴はレキシカル変数以外の全ての変数は必ずどこかのパッケージに所属しているからパッケージ修飾無しでの真にグローバルな変数なんて存在しない。
逆にパッケージで修飾さえすればそれは全てグローバル変数になる。
まぁあえて言うならmainパッケージの変数をグローバル変数とするかな?

 # Ruby
 $foo = 10
 
 # Perl
 $main::foo = 10

まぁ、危険すぎるんでおそらくRubyグローバル変数は使うことは無いでしょう。

  • 疑似変数

これはもうドキュメントの通りでしょう。
selfとかtrueとか__FILE__とかの事を疑似変数というらしいです。

ところでふと気になったことがあってPerlで「これ以降をコンパイルしない」ということをやってくれる__END__がRubyでもできるかなーとか思って何気にやってみたらなんかPerlを同じ挙動になってるっぽいんだけどこれって使えるの?
ちゃんと調べてないので後でRubyの__END__について調べてみようかな。

  • 定数

大文字のアルファベットで始まる識別子は定数となる。

クラス変数と似てるんだけど違う部分がある。

まず変数への値の再代入ができない。
いや、正確に言うとできないわけではなく、再代入すると警告が出るだけみたい。

そしてもう一つの違い。
それは定数がクラスごとに定義されるので例えばFooクラスでFOO定数を定義すればそれはFoo::FOOとなる。

 class Foo
   FOO = 1
 end
 p Foo::FOO

クラス内部から定数を利用する場合はFooで修飾しなくても良い。修飾しなかった場合はカレントのクラスの定数が呼び出されていることになる。

 class Foo
   FOO = 1
   
   #  どちらでもOK
   p FOO
   p Foo::FOO
 end

で、もしカレントのクラスに定数が定義されていなかった場合は継承ツリーをたどって基底クラスに同名の定数が定義されているか見に行く

 class Foo
   FOO = 1
 end
 class Bar < Foo
   p FOO # 1
 end

そして注目すべき点はここ!上記の例のBarクラスでFOOを定義した場合、それはFOO定数の再代入にはならずにBar::FOO定数を定義したことになり、Foo::FOOとBar::FOOというように別の定数として定義される!

 class Foo
   FOO = 1
 end
 class Bar < Foo
   FOO += 1
   p FOO      # 2
   p Bar::FOO # 2
   p Foo::FOO # 1
 end

おお!これってまさにClass::Data::Inheritableじゃないですか!
良いねーRuby。僕の大好きなClass::Data::Inheritableが実現できるじゃん。

さっきクラス変数の時に書いた例を定数で書き直してみよう

class Foo
  FOO = 1
  def get()
    return FOO
  end
end
class Bar < Foo
  FOO += 1
  def get()
    return FOO
  end
end
class Baz < Bar
  FOO += 1
  FOO += 1
  def get()
    return FOO
  end
end

p Foo.new.get # 1
p Bar.new.get # 2
p Baz.new.get # 3

うん、期待通りの結果になったな。
良いね良い。

まぁ、とはいえRubyの場合、あくまでも定数ということで再代入はご法度なのでその辺にちょっとした違いがあるので要注意。

さて、とりあえず変数に関して一通りざっくりと勉強が終わりました。

多分それほど間違った解釈はしてないとは思いますがとりあえずこんなもんで。

# 三日坊主にならないように・・・。