Rubyでプログラミング入門 RSSフィード

2006-11-07

[]結婚式を挙げました

私事もいいとこで恐縮です。日曜日に挙式してきました。

勉強どころじゃない日々でしたが、おかげさまでとても素晴らしい日になりました。

あれだけ多くの方に祝福され、多くの方に感謝する日は二度とないような気がします。

ひととおり良い思いをさせて頂いたので、いい加減気持ちを切り替えて頑張ります。


あ、あと。

はてなパーカー欲しい!

[]クラスとメソッド

メソッド < オブジェクト < クラス

前回演算子式を学んだ際、気になる部分を残していました。

ほとんどの演算子は特別な形式のメソッド呼び出しですが、一部のものは言語に組み込みで、再定義できません。

さて、演算子の正体は特別な形式のメソッドだと云われています。そのあたりについて突っ込んでみてみましょう。


まずは、メソッドとはそもそもなんだったでしょうか。

メソッドとは 【method】 - 意味・解説 : IT用語辞典 e-Words

 オブジェクト指向プログラミングにおいて、各オブジェクトが持っている自身に対する操作。オブジェクトは「データ」と「手続き」から成っているが、その「手続き」の部分に当たる。プログラミング言語によっては「メンバ関数」と呼ばれることもある。

 オブジェクト指向では、オブジェクトの持つデータを操作する方法はオブジェクト自身がメソッドとして内蔵しており、これを外部から呼び出すことによって操作を行なう。こうすることにより、操作の詳細をオブジェクト内部に隠蔽することができ、プログラムの再利用性や生産性を高めやすくなると言われている。

むむ。

オブジェクトが持っている自身に対する操作』だそうですね。

それなら“オブジェクト”とは何なのでしょうか。

オブジェクトとは 【object】 - 意味・解説 : IT用語辞典 e-Words

 オブジェクト指向プログラミングにおいては、ソフトウェアが扱おうとしている現実世界に存在する物理的あるいは抽象的な実体を、属性(データ)と操作(メソッド)の集合としてモデル化し、コンピュータ上に再現したもの。オブジェクトを定義するモデルはクラスと呼ばれる。クラスに基づいて実際にコンピュータのメモリ上に展開されたオブジェクトのことをインスタンス(実体)と言うが、このインスタンスの意味でオブジェクトと呼ぶ場合も多い。

くらくらしてきました。カタカナと漢字の多い解説で涙が出ます。

順番に考えてみます。


  1. オブジェクトとは、現実世界に存在する実体をモデル化し、コンピュータ上に再現したもの
  2. そのモデルはクラスと呼ばれる
  3. クラスに基づいて実際に展開されたオブジェクトインスタンス(実体)と云う
  4. オブジェクトインスタンス

……という感じでしょうか。

取り敢えず“クラス”とやらも調べてみてから考えましょう……

クラスとは 【class】 - 意味・解説 : IT用語辞典 e-Words

 オブジェクト指向プログラミングにおいて、データとその操作手順であるメソッドをまとめたオブジェクトの雛型を定義したもの。これを定義することで、同種のオブジェクトをまとめて扱うことができるようになる。クラスに対して、具体的なデータを持つ個々のオブジェクトは「インスタンス」と呼ばれる。なお、クラスの定義を他のクラスに受け継がせることを「継承」と言う。その際、元になるクラスを「スーパークラス」(super class)、あるいは「基底クラス」「基本クラス」(base class)などと呼び、新たに定義されたクラスを「サブクラス」(subclass)、あるいは「派生クラス」(derived class)と呼ぶ。

orz


これらの説明から、なんとか糸口を見つけなければなりません。

ぼーっと眺めてみると、3つの説明文に共通する記述があることに気が付きました。

・メソッドの説明文

オブジェクトは「データ」と「手続き」から成っている


オブジェクトの説明文

属性(データ)と操作(メソッド)の集合としてモデル化


・クラスの説明文

データとその操作手順であるメソッドをまとめたオブジェクトの雛型


……と。

言い回しは少しずつ違いますが、

  • データ(属性)
  • メソッド(操作)

という2点が繰り返し出てきていますので、これが重要なポイントなのではないかと思いました。


データとメソッド

そもそもプログラミングというものは、実際に存在している解決すべき問題を“プログラミング言語”によって表現し、コンピュータにその解決を依頼する行為だと云えます。

その際には、現実世界に存在する(物理的・抽象的な)モノが関わってきます。

このような“モノ”のことを、オブジェクトインスタンス)と呼んでいるようです。

オブジェクトはクラスと呼ばれるモデルに基づいて作られる実体、でした。逆に言うと、クラスはオブジェクトの雛型です。

つまり、オブジェクトがどのような特性を持っているのかは、クラスが知っているということです。

それではクラスはどのような存在かというと、『データと、その操作手順(メソッド)をまとめて定義したもの』だという話でした。


実例で考えてみましょう。せっかくなので、以前利用した自動販売機さんに再度登場願いましょう。


自動販売機プログラム

自動販売機シミュレートするプログラムを考えてみます。まずは、その中に登場するモノを検討します。

簡単に思いつくのはこんなとこでした。

これらは実際に存在するもの(実体)ですので、オブジェクトインスタンス)ということになりそうです。

ということは、これらを定義するクラスが必要だ、ということです。

ということで良いのでしょうか。*1


さて、クラスは「データとその操作手順(メソッド)」を持つのでした。

それでは自動販売機クラスを例にとって、データとメソッドを検討してみます。

まず簡単にわかりそうなデータは、

  • 飲み物(複数)

ではないでしょうか。

自動販売機ですから、飲み物を持っててくれないと困ります。

続いて自動販売機の操作ですが、

  • お金を入れる(というか入れられる?)
  • ボタンを押す(押される?)
  • 飲み物を出す
  • お金を返す

とかこんな感じでしょうか。自動販売機の立場に立って考えるところなのかどうかわからなくて、言い回しが微妙です。


とりあえず、ここまで検討した内容でシミュレートしてみます。

f:id:mailishmaid:20061107154955j:image

  1. まず、お金を入れます
  2. 次に、欲しい飲み物のボタンを押……

いきなり躓きました。何と見通しの甘いことでしょう。

冷静に考えてみると、ちょっと情報が少なすぎるような気がします。

  • 今現在、いくら自動販売機に入れてあるのかわからない
  • どの飲み物を買える状態にあるのかわからない

これでは使い勝手が悪すぎます。この二つの情報を自動販売機が管理していなければいけません。

今一度考え直してみると、自動販売機クラスが持つデータは、

  • 飲み物(複数)
  • 現在投入済みのお金

となります。


あれ、「どの飲み物が買えるかどうか」はデータとして持たなくて良いのでしょうか?


色々と考えてみたのですが、どの飲み物が買えるかという情報は、既に自動販売機が持っている「飲み物(複数)」と「現在投入済みのお金」から自動的に計算されるような気がしたのです。

必要であれば計算で求められるので、わざわざ計算結果を自動販売機が常に持っていなくてもよさそうです。

そのかわり、「現在買える飲み物はどれかを知らせる機能」を自動販売機に持たせてあげようと思います。


ということで自動販売機の機能(操作、メソッド)は、

  • お金を入れられる
  • ボタンを押される
  • 飲み物を出す
  • お金を返す
  • 現在投入済みの金額を表示する
  • 現在購入可能な飲み物を知らせる

という感じに増えました。

投入済み金額をデータとして管理することにしたので、それを表示する機能もついでに追加しています。


f:id:mailishmaid:20061107154952j:image

うーん、これだけ用意してあればなんとなくそれなりのシミュレーションはできそうです。


まとめ

[]リテラルの正体を暴く

演算子はメソッドの呼び出しですよ、というところからはじまって、オブジェクトやクラスの話になってしまいました。

もう一度演算子のところに戻ってみましょう。

11 + 5

たとえばこんな何の変哲も無い足し算すら、メソッドの呼び出しである、という主張でした。

今の私は、メソッドがあるからには、そこにはその『メソッドを持っているオブジェクトがある』はずだ、ということを知っています。

そうは云っても、現在『 + 』のほかの登場人物といえば『 11 』と『 5 』だけですね……

普通に考えてみたら、このどちらかが、『 + 』のメソッドを持っているはずです。


しかし、今回は同じ数値リテラルですから、どちらで考えても同じことです。結局、数値リテラルも実はオブジェクトだよ、ということになるのではないでしょうか。

オブジェクトということは、その基になるクラスが必ずあります。


それでは『 11 』や『 5 』は何というクラスのインスタンスなのでしょうか。

その正体は、『Fixnum』クラスです。

Fixnum


ほんとかよ、って云われると困るので、ちょっと話が飛びますがオブジェクトのクラスを知る方法を紹介します。

例えば『 11 』のクラスが知りたければこのようにします。

puts 11.class

↓実行結果

Fixnum

このように、

<オブジェクト>.class

という形式で、そのオブジェクトが属するクラスを取得することができます(言い方を変えると、classというのはクラス名を返却するメソッドです)。

これを実行すれば、『 11 』はFixnumクラスのインスタンスである、ということが確認できると思います。


同じように、"Hello"のような文字列リテラルや、3.14のような浮動小数点数でもクラス名を確認できます。

puts "Hello".class
puts 3.14.class

↓実行結果

String
Float

文字列リテラルはStringクラスのインスタンスで、浮動小数点数はFloatクラスのインスタンスのようです。

これらはそれぞれ、String型・Float型というような言い方をされることもあります。


ともあれ、数値リテラルオブジェクトで、四則演算しているときも実際は定義されているメソッドを呼び出しているんだよ、というのがなんとなくわかってきました。

ついでなので、本当にそんなメソッドが定義されているのかも見てみたいと思います。

オブジェクトの持つメソッドを知るには、たとえば次のようにします。

puts 11.methods

methodsというのは、オブジェクトの持つメソッドの一覧を返却するメソッドで、

<オブジェクト>.methods

という形式で呼び出すことができます。

実行するとずらずらっと色々表示されて、その中に『 + 』とか『 / 』とか『 class 』とか『 methods 』自身も確認できると思います。

演算子も確かにメソッドでした……ふーむ。


ゆとり教育の正体は?

そういえば前回、

puts 11 / 3

を実行したら

3

が出てくる、ということで文句を云ってみました。

これについては通りすがりさんが

11 / 3 は、整数同士の割り算だから、答えも整数

小数点の答えをほしい場合はどちらかを小数にする必要がある。

puts( 11.0 / 3 )

ここからへんは、数値クラスについて、マニュアルにいろいろと書いてある。

とコメントで教えてくださいました。ありがとうございます。

そんなわけで早速マニュアルを見てみましょう。

数値関連クラスの定義メソッド

ここまでで、数値関連のクラスではFixnumとFloatを確認しています。

Rubyにおける数値型は、実際にはNumericというクラスを頂点とした継承関係を築いているようです。

前回の問題は結局のところ、『Fixnumクラス(というかIntegerクラス)の / メソッドは、引数がIntegerであるとき、Integerの結果を返却する』ように定義されている、ということなのだと思います。

通りすがりさんがおっしゃっているように、どちらかのオペランドの型をFloatにすれば解決です。


レシーバ

ここまでなんの断りもなく書いてきましたが、一応確認しておきます。

オブジェクトのデータやメソッドを表す場合は、

オブジェクト.データ
オブジェクト.メソッド

という具合に、.(ドット:ピリオド)で繋いで関係を表現します。

このとき、.の左側にあるものを“レシーバ”と呼びます。

このあたりは、リファレンスマニュアルを確認すると良さそうです。

メソッド呼び出し


さらりと書いちゃってますが、意外と重要です。レシーバ。すいません。


さてさてこんな風に呼び出すメソッドですが、以前数値を10進数以外の形式で表示させた際にも、このようにしてちゃっかりメソッドを利用しました。

puts '■10進整数\n'
puts 123.to_s(16)  # 16進表記
puts -123.to_s(8)  #  8進表記
puts 0d123.to_s(2) #  2進表記

puts '■16進整数'
puts 0xffff.to_s(16)

puts '■2進整数'
puts 0b1011.to_s(2)

puts '■8進整数'
puts 0377.to_s(8)
puts 0o377.to_s(8)

ここで使っているto_sメソッドは、引数で指定した基数による文字列表現に変換するメソッドです。

このような『to_なんとか』メソッドは、Rubyでは(なんとか型に)変換を行うメソッドに付けられる名前になっています。

to_sのsはString(文字列)のsなので、文字列表現に変換、という機能になるわけですね。


同じような感じで、to_f(to Float)なんていうメソッドもあって、これを使った場合だとFloatに変換できるわけです。

ということは、

puts (11 / 3).to_f

とすればいい感じの結果が!

3.0

……でません。


既に計算してしまった結果に対して変換をかけているので、あたりまえですね。

なので、こんな風にします。

puts (11.to_f / 3)
puts (11 / 3.to_f)
puts (11.to_f / 3.to_f)

これで完璧です。要するに演算前(メソッド呼び出し前)に適切な型にしておく必要があるよ、ということです。

でもまぁ素直に浮動小数点数リテラル使えばいいと思います。

[]オブジェクトとかクラスとか

いわゆるオブジェクト指向という考え方の中に登場する、基本中の基本の言葉について少し触れました。

これって用語がわかれば良いという話ではなくて、なかなか感覚を掴むまでが苦労する気がします。


オブジェクト指向感覚を得るには、『憂鬱なプログラマのためのオブジェクト指向開発講座』がとても良いです。

解説にはC++言語を使っているのですが、C++を知らなくても、むしろついでにC++を学べてしまうくらいで理解しやすい内容だと思います。

憂鬱なプログラマのためのオブジェクト指向開発講座 (DDJ Selection)

憂鬱なプログラマのためのオブジェクト指向開発講座 (DDJ Selection)

*1:そういえば以前は自動販売機は『関数』として考えたのでした。しかし、実はあれはクラスとして考えるともっとしっくりくる、という事だったのです……

Connection: close