数学の世界で1は数である。3歳の子供でもそれを知っている。そして私やあなたが老いて死にゆくまで1は数であり、そこに疑念の入る余地はない。

プログラミングの世界でもふつう、1は数である。CでもJavaでもHaskellでも1は数であり、それ以上でもそれ以下でもない。

ところが驚くべきことに、Rubyの世界では1は数ではないのである。

嘘だと思うならirbを立ち上げて、次のようにしてみるといい。

$ irb
>> 1.next
=> 2

あなたは今、1にnextというメッセージを送った。そうしたら1は2という答えを返したのだ。つまり、Rubyの世界で1は数以上のものであり、メッセージに返答する「何か」である。

Rubyの世界でそれは「オブジェクト」と呼ばれている。

1はメッセージに反応するオブジェクトである。

しかしここで一つの疑問が湧いてくる。それならば今、1が返した2というのは何なのか。数なのかそれともオブジェクトなのか。

この疑問にもirbが答えてくれる。

>> 1.next.next
=> 3

1からの返答に、さらにnextというメッセージを送ってみる。つまり1.nextで返される2にメッセージを送ってみると、果たして3が返ってきた。そう、1から返答された2もやはりオブジェクトだったのだ。

疑い深いあなたはこれだけでは納得しないかも知れない。そしてirbで、きっと次のように入力するのだろう..

>> 1.next.next.next.next.next.next.next.next.next.next.next.next.next.next.next.next.next.next.next.next.next.next.next.next.next.next.next.next.next.next.next.next.next.next.next.next.next.next.next.next.next.next.next.next.next.next.next.next.next.next.next.next.next.next.next.next.next.next.next.next.next.next.next.next.next.next.next.next.next.next.next.next.next.next.next.next.next.next.next.next.next.next.next.next.next.next.next.next.next.next.next.next.next.next.next.next.next.next.next
=> 100

納得した?

そうRubyの世界では入り口も出口も、そのすべてがオブジェクトなのである。

さて、1がオブジェクトならnext以外のどんなメッセージに反応するのかが気になるところだ。Rubyではその答えも1に聞けばいい。methodsというメッセージを1に送ってみよう。

>> 1.methods
=> [:to_s, :-@, :+, :-, :*, :/, :div, :%, :modulo, :divmod, :fdiv, :**, :abs, :magnitude, :==, :===, :<=>, :>, :>=, :<, :<=, :~, :&, :|, :^, :[], :<<, :>>, :to_f, :size, :zero?, :odd?, :even?, :succ, :integer?, :upto, :downto, :times, :next, :pred, :chr, :ord, :to_i, :to_int, :floor, :ceil, :truncate, :round, :gcd, :lcm, :gcdlcm, :numerator, :denominator, :to_r, :rationalize, :singleton_method_added, :coerce, :i, :+@, :eql?, :quo, :remainder, :real?, :nonzero?, :step, :to_c, :real, :imaginary, :imag, :abs2, :arg, :angle, :phase, :rectangular, :rect, :polar, :conjugate, :conj, :between?, :nil?, :=~, :!~, :hash, :class, :singleton_class, :clone, :dup, :initialize_dup, :initialize_clone, :taint, :tainted?, :untaint, :untrust, :untrusted?, :trust, :freeze, :frozen?, :inspect, :methods, :singleton_methods, :protected_methods, :private_methods, :public_methods, :instance_variables, :instance_variable_get, :instance_variable_set, :instance_variable_defined?, :instance_of?, :kind_of?, :is_a?, :tap, :send, :public_send, :respond_to?, :respond_to_missing?, :extend, :display, :method, :public_method, :define_singleton_method, :__id__, :object_id, :to_enum, :enum_for, :equal?, :!, :!=, :instance_eval, :instance_exec, :__send__]

これらは1が応答できるメッセージの集合で、Rubyでは「メソッド」と呼ばれている。つまり1はあなたからの問い合わせに対し、これら130個ものメソッドで応答する。

クラス

上の説明を聞いてあなたは「Rubyの1って天才、スゲー」とつぶやいたに違いない。

しかし事実はそうではない。実は1の中身は私やあなたのあたまの中同様、ほとんど空っぽなのである。空っぽなのに1はこれらのメッセージに反応できるのである。

このカラクリは難しいものではない。Rubyにおいて1は、先のメソッドの集合を実際に持っているFixnumというクラスにリンクされている。そして1があなたからのメッセージを受け取ると、それをFixnumに渡してその返答が得られたら、1はそれをあなたに返しているだけなのだ。

つまり1は、「ググって」ばかりの私やあなたと同じで、問い合わせのたびにただ「クラスってる」だけで FixnumこそがGoogle同様のスゲー存在なのである。そして私やあなたはWebの世界ではRubyの1みたいな存在なのである..

Ruby設計者がこのような仕組みを採用した理由は容易に想像がつくだろう。そう、Rubyでは2も3も109も17320508もFixnumというクラスにリンクしていて、これらの数に対する問い合わせがあったときには、すべてFixnumが答えを用意しているのだ。そうすればこれらの数が、それぞれ個別に130個のメソッドを持つ必要はなくなる。

メソッド定義

Rubyで「プログラミングをする」というのは、「オブジェクトにメッセージを送る」とほぼ同義である。自由で柔軟なプログラミングを実現するためRubyの設計者は大量のメソッドを用意した。しかしもちろんそれだけでは、真に自由なプログラミングができるわけではない。独自メソッドが定義できてこそ、本当のプログラミングが実現できる。そしてRubyはそれをあなたに許す。

今あなたが1に挨拶したら返事がほしいとしよう。1の中身は空っぽでFixnumクラスがメソッドを持っていることを思い出そう。そう、あなたのメソッドもFixnumクラスに追加すればいい。

>> class Fixnum
>>  def hello
>>    "Yo!"
>>  end
>> end

さあ あなたから1に挨拶を!

>> 1.hello
=> "Yo!"

もう少し気の利いた返事がほしいなら、メッセージが別のオブジェクトを受け取れるようにすればいい。

>> class Fixnum
>>   def hello(name)
>>     "Yo! #{name}"
>>   end
>> end

そしてあなたの名前を渡す。

>> 1.hello("Charlie")
=> "Yo! Charlie"

ここまでの説明が理解できたなら、挨拶に答えられるようになったのが1だけではないということが分かるだろう。

>> 2.hello("Ken")
=> "Yo! Ken"
>> 24.hello("ジャック・バウアー")
=> "Yo! ジャック・バウアー"
>> 365.hello(365)
=> "Yo! 365"

そう今やFixnumクラスに属するすべての数字が、あなたが作ったhelloに答えられる。

image

上のコードの最後の答えを見てドキリとする人もいるかもしれない。メソッドに渡すオブジェクトを「引数」と呼ぶけれども、渡されるものがオブジェクトである限りRubyはそれを引数として受け入れる。上の2つは文字列のオブジェクトを引数として渡し、最後のものは今まで見てきた数のオブジェクトを渡している。すでにオブジェクトからの応答がオブジェクトであることを理解したあなたなら、次のコードも理解できるだろう。

>> 1.hello(2.hello("Ma"))
=> "Yo! Yo! Ma"

続き



blog comments powered by Disqus
ruby_pack8

100円〜で好評発売中!
M'ELBORNE BOOKS