読者です 読者をやめる 読者になる 読者になる

Rubyについて

こんにちは、一周して二度目の投稿です。
moguranosenshiがTreasureについて良い事書いてて、自分は方向性を間違っちゃったかなーとか思ってましたが、だっちがしっかり技術について書いてたので僕らが多数派になりました。
ありがとうだっち。安心してrubyについて書ける。

プログラミング言語Ruby

さて、rubyです。この前railsについて中途半端に説明しておきながら、このタイミングでrubyです。
もともとはrailsのviewとかcontrollerとかについて引き続き書く予定だったんですが、そもそもrubyについて知らないとその上に乗っかってるフレームワークについてごちゃごちゃ言われても困るだろうと思って書くことにしました。
僕は個人的にrubyが大好きなんですが、特に「全てがオブジェクトで全てがメソッド」という一貫性のある構造と、「メタプログラミングが容易に可能」な言語仕様の二点に魅力を感じています。
順に説明してみたいと思います。

オブジェクトとメソッド

まず、ここで言うオブジェクトとメソッドが何なのか、はっきりさせておきましょう。
オブジェクトとは、データとそのデータに関連するメソッドがひとかたまりとなったものです。メソッドとは、オブジェクトに紐付いた関数です。

例えば、

class MyClass
  def my_method
  end
end

my_object = MyClass.new
my_object.my_method

みたいな感じでサクッと書いた時に、my_objectがオブジェクト、my_methodがmy_objectに関連付けられたメソッドという事になります。

ただし、rubyのオブジェクトは上のようにクラスのnewメソッドで作られたものだけはありません。
例えば、上のclass式によって作られたMyClassクラス自身もオブジェクトです。文字列もオブジェクトですし、arrayやhashも皆オブジェクトです。また、コードが実行されるコンテキスト自体もmainという名のオブジェクトで、それは

print self
>> main

の様にprint selfでmainが出力される事から確認出来ます。尚、selfというのはその時のコンテキストとなっているオブジェクトが格納されている変数です。

そして注目すべき事に、rubyにはメソッドでは無い関数というものは存在しません。例えば、上の例で出てきたprintも、mainオブジェクトのメソッドです。多くの言語であればただの関数として定義するprintを、きちんとメソッドとして定義して一貫性を保っているのは面白いと思います。

また、printの例で分かるように、コンテキストに紐付いたメソッドはメソッド名だけで呼び出せます。例えば、class定義の中ではそのクラスのメソッド(クラスメソッドと呼ばれる)はメソッド名だけで呼び出せます。前回の記事で書いたattr_accessibleというのも、クラスメソッドだった為にattr_accesible :hogeという形で使えた訳です。

rubyのメソッドには、引数を括弧で囲まなくても良いという著しい特徴も存在します。上のprintメソッドやattr_accessibleというのがいい例ですが、引数をそのまま続けて書いています。この方が構文っぽくなってカッコ良いというのもありますが、それだけでは無く、rubyのオブジェクトにおける「属性(attribute)」というものを考える際にもこの性質は深く関わってきます。

実は、rubyのオブジェクトはc++pythonで言うところの属性というものは言語の構造としては持っていません。例えば、pythonでは<オブジェクト名>.<属性>という構文を使ってオブジェクトの属性にアクセス出来ます。属性として文字列や数値がバインドされていればそのまま扱えますし、関数がバインドされていればそれはすなわちメソッドとなります。ただし、ただ単に属性にアクセスしても関数オブジェクトが返ってくるだけであり、メソッドを呼び出すには括弧をつける必要があります。

一方、rubyではそもそも属性というものがオブジェクトには存在しない為、<オブジェクト名>.の後に続くのは必ずメソッド名となります。それゆえ、括弧をつけようとつけまいとメソッドを呼び出していると判断出来る訳です。

ただし、その場合には一つ問題が生じます。それは、オブジェクトに紐付いたデータをどうやって取り出すのかという事です。答えを言いますと、rubyではgetterやsetterが必要となります。すなわち、例えばmy_objectにmy_numという属性(のように見えるもの)を持たせる場合には、

class MyClass
  def initialize(num)
    @my_num = num
  end

  def my_num
    @my_num
  end

  def my_num=(num)
    @my_num = num
  end
end

のようにsetterであるmy_numメソッドとgetterであるmy_num=メソッドを定義する事になります。
尚、initializeメソッドはnewメソッドによってオブジェクトを生成する際に呼び出されるメソッドで、コンストラクタみたいなやつです。また、@my_numはインスタンス変数と呼ばれるもので、オブジェクト(インスタンス)に紐付いているけれどインスタンスメソッド(上の例で言うinitializeやmy_numやmy_num=)からしかアクセス出来ない変数です。
この様にsetterとgetterを用意する事で、

my_object = MyClass.new(3)
print my_object.my_num
>> 3
my_object.my_num = 4
print my_object.my_num
>> 4

の様に、はたからは属性の様に見えるmy_numを作る事が出来ます。

ただ、こんな面倒な事、普通はやってらんないですよね...
属性が一つ増えるたびにgetterとsetterをいちいち書くとか、正気の沙汰ではありません。そう、ちゃんと簡単な方法は用意されているのです。それが、attr_accessorと呼ばれるクラスメソッドです。

class MyClass
  def initialize(num)
    @my_num = num
  end

  attr_accessor :my_num
end

の様に、attr_accessorメソッドに引数として属性名(のシンボル)を渡してやる事で、getterとsetterが作られます。(尚、シンボルというのは先頭にコロン「:」がついたヤツです)。言語固有の構文っぽく書けるのに実は単なるクラスメソッドってのがめちゃめちゃカッコいいですね。

attr_accessorはただメソッドを定義しているだけなのですが、メソッドを定義するメソッドであり、いわゆる「メタプログラミング」に相当します。rubyは言語仕様としてメタプログラミングがしやすくなっており、こういった似たような処理を行うメソッドを大量に作りたい時にメタプログラミングは大活躍します。ただ、今回も思った以上に長くなっちゃったのでメタプログラミングについては今度書くことにします。

進行がグダグダになっちゃってますが、一応今の予定としては次回はメタプログラミングについて、その次はまたrailsについて続きを書いていきたいと思います。
じゃ、そんな感じでまた今度!