第4回(10/23) 継承 ・単純継承、Mixin、モジュール ☆ 動物クラス、(たぶん、今日はここまで) ☆ 自動車クラス、エンジンクラス、パーツモジュール ■ Ruby クラス階層図 Object ├─ Array ├─ Binding ├─ Continuation ├─ Data ├─ Dir ├─ Hash ├─ IO │ ├─ File │ └─ BasicSocket │ ├─ IOSocket │ │ ├─ TCPSocket │ │ │ └─ TCPServer │ │ └─ UDPSocket │ ├─ UNIXSocket │ │ └─ UNIXServer │ └─ Socket ├─ MatchData ├─ Method ├─ Module │ └─ Class ├─ Numeric │ ├─ Float │ └─ Integer │ ├─ Bignum │ └─ Fixnum ├─ Proc ├─ Range ├─ Regexp ├─ String ├─ Struct ├─ Symbol ├─ Thread ├─ ThreadGroup └─ Time ■ 継承 ・クラスの性質を引き継ぐ。 ・上位クラス・Superクラス:引き継がれるクラス ・階層図によると、String というクラスは、Objectクラスの下に位置する。 = Objectクラスを継承している。 = Objectクラスのメソッドを使うことができる。 ⇒ list0401.rb ・a.kind_of?(b) :オブジェクトaが、オブジェクトbあるいはその子孫のインスタンスであるか調べる □□□ 演習1 □□□ (a) 文字列型のインスタンス a を作り、a の持つメソッドを調べよ。具体的には、コマンド irb で、ruby のインタプリタを起動し、a に methods というメッセージを送れば良い。 (b) 同様に、a にメッセージ instance_variables を送り、インスタンス変数を一覧せよ。 (c) 前回作った図形クラスのクラス宣言部を、ファイル prac0401.rb にコピーする。コマンド irb で、ruby のインタプリタを起動し、load "prac0401.rb" でプログラムを読み込む。t1 を三角形クラスのインスタンスとしたとき、t1 のメソッドおよびインスタンス変数を一覧せよ。 (d) (c)の続き。Triangle クラスは何クラスの子孫のインスタンスであるか調べよ。調べ方は、Triangle.kind_of?(何とかクラス) を実行する。「何とかクラス」には、クラス階層図を参考にせよ。 ※ ねらい:(a) (b) は引き継がれる内容を調べること。(c) は自分で作ったオブジェクトを調べること。(d) は組み込みのオブジェクトの継承関係を確認すること。 ⇒ demo0401 ● 単純継承 ・Ruby は、単純継承のみ許され、複数の親を持つ継承は許されていない。 ・プログラムの記法 ⇒ list0402.rb □□□ 演習2 □□□ 次の3つのクラスを作れ。 - 次のメソッドを持つ動物クラス Animal を作れ。Animal は、背骨を持つのでメソッド has_bone? は true を返す。肺呼吸なのでメソッド breath は lung を返す。 - Animal の下位に、次のメソッドを持つ犬クラス Dog を作れ。犬は、4本足で歩くので、メソッド walk は、four_legs を返す。 - Animal の下位に、次のメソッドを持つ人間クラス Human を作れ。人は、2本足で歩くので、メソッド walk は、two_legs を返す。 メインルーチンの例: pochi = Dog.new p pochi.has_bone? p pochi.breath p pochi.walk taro = Human.new p taro.has_bone? p taro.breath p taro.walk ※ ねらい:クラスの継承を自分で作ってみること。その動作を確認すること。 ⇒ prac0402.rb ■ Mix-in:(多重継承を許さない代りに) オブジェクト.ancestors でオブジェクトの祖先が一覧できる。 ・Object.ancestors ⇒ [Object, Kernel] ・Numeric.ancestors ⇒ [Numeric, Comparable, Object, Kernel] ・String.ancestors ⇒ [String, Enumerable, Comparable, Object, Kernel] Kernel、Comparable, Enumerable は、クラスではなく、モジュールである。 1つのクラスは1つのクラスを継承する(親の親は有り)。多重継承しない(2人の親はダメ)。 1つのクラスは複数のモジュールを持つことができる。 モジュールは、機能(メソッド)の塊である。 機能の塊は、性質の異なるところ(クラス)で、共通に使われる。 ■ モジュール 複数のメソッドをまとめるオブジェクト。クラスとの違いは、 ・class 宣言ではなく、module 宣言を使う。 ・継承できない。 ・インスタンスを作れない。 (逆に言えば、Class クラスは、Module クラスの機能に、継承・インスタンス化の機能を追加している。Ruby クラス階層図参照) ⇒ list0403.rb □□□ 演習3(モジュールは、要するにパーツ) □□□ 次のとおりクラスとモジュールを定義せよ。 自動車クラス Car では、メソッド has_an_engine?、has_wheels?、は true を返し、has_a_cdplayer?、has_a_navi?、has_an_etc? は false を返す。Carの下位クラスにT社自動車クラスTCar、H社自動車クラスHCar、D社自動車クラスDCar、S社自動車クラスSCar、がある。 電気機器メーカにDN社とGA社があり、DN社モジュール DenAudio、GA社モジュール GaAudio がある。DenAudio のメソッド has_a_cdplayer?、has_an_etc? は true を返し、has_a_navi? は false を返す。GaAudio のメソッド has_a_cdplayer?、has_a_navi? は true を返し、has_an_etc? は false を返す。 ここで、T社自動車とD社自動車は DenAudio を採用し、H社自動車は GaAudio を採用している。 メインルーチンでは、各変数 mrs、s2k、cpn、jmy に TCar、HCar、DCar、SCar のインスタンスを格納し、has_an_engine?、 has_wheels?、has_a_cdplayer?、has_a_navi?、has_an_etc? の結果を出力せよ。 メインルーチンの例: mrs = TCar.new s2k = HCar.new cpn = DCar.new jmy = SCar.new [mrs,s2k,cpn,jmy].each{|car| puts '-----' p car.has_an_engine? p car.has_wheels? p car.has_a_cdplayer? p car.has_a_navi? p car.has_an_etc? } ⇒ prac0403.rb □□□ 演習4 □□□ 自動車エンジンの馬力は、吸気系、エンジン本体、排気系、の総合性能で決まる。 そこで、E07Aというエンジンをクラスで定義する(詳しくは、ファイル e07a.rb)。 さて、Ht社は改造パーツを販売している。排気系パーツをモジュール HtMufflerで、エンジンパーツをモジュールHtHiCompで、それぞれ表わすには、以下のように定義すれば良い。 module HtMuffler module HtHiComp def exhaust_type def body_type return '75phi muffler' return '696cc + HiCAM' end end def exhaust_power def body_power return 11 return 60 end end end end ※ 紙面の都合で、横に並べてます。プログラムを書くときは縦に並べてください。 (a) Ht社は E07A に HtMuffler と HtHiComp を装着して販売している。そのエンジンクラスを HtE07A として、定義せよ。 ヒント: prac0404a.rb のファイル ーーー ここから ーーー class E07A 宣言の詳細は、e07a.rb よりコピー end # モジュール宣言部 ????????? : : ????????? # クラス宣言部 class HtE07A ?????? ??????? ????????? ??????? ???????? end # 動作確認用のメインルーチン e1 = E07A.new e1.show e2 = HtE07A.new e2.show ーーー ここまで ーーー ※ 64馬力と76馬力と表示されれば正解。 ⇒ prac0404a.rb (b) M社の吸気系パーツMFunnel(タイプ'NA+Funnel'、パワー 9)、R社の吸気系パーツRAirCleaner(タイプ 'NA+Cyl'、パワー 7)、T社の排気系パーツ TMuffler(タイプ 'CFR muffler'、パワー 12)、C社の排気系パーツ CMuffler(タイプ'titan muffler'、パワー14)、と様々なパーツがある。それぞれモジュールで定義せよ。また、無改造のパーツとして、吸気系ノーマルパーツのモジュール NAsp(タイプ'normal NA'、パワー5)、排気系ノーマルパーツのモジュール NExh(タイプ'normal Ex'、パワー9)、を定義せよ。 ヒント: prac0404a.rb のモジュール宣言部を強化する。 module MFunnel def aspiration_type return 'NA+Funnel' end def aspiration_power return 9 end end module RAirCleaner - - - end module TMuffler - - - end module CMuffler - - - end module NAsp def aspiration_type return 'normal NA' end def aspiration_power return 5 end end module NExh - - - end ※ メインルーチンは特に無し ⇒ prac0404b.rb (c) T君は、E07A を持っている(e3 = E07A.new)。とりあえず、これに排気系パーツのみを装着した。これを特異Mix-in( e3.extend(HtMuffler) )で実現できる。動作結果を確認せよ( e3.show )。 ヒント: prac0404b.rb のメインルーチンを説明の通りに作る。 ⇒ prac0404c.rb (d) 吸気系パーツの配列 alist、排気系パーツの配列 elist を作る。各要素数は、3個と4個になるので、組合せ数は、12通りになる。各組合せについて、パワーの大きさを調べ、最もパワーのあるモジュール名の組とそのパワーを表示せよ。 ヒント:prac0404b をベースにして、メインルーチンを以下のように作る。 配列変数 alist に吸気系のパーツモジュールを3つ入れる 配列変数 elist に排気系のパーツモジュールを4つ入れる E07A のインスタンス e0 を作る 最大値の変数 max は、e0 の馬力とする 最大値となるパーツの組 maxset は、吸排気系の各ノーマルパーツモジュールとする alist の各要素 asp について以下を行なう elist の各要素 exh について以下を行なう E07A のインスタンス e を作る 特異 Mix-in により e に asp のモジュールを取り付ける 特異 Mix-in により e に exh のモジュールを取り付ける e の馬力 pwr を求める pwr と max を比較して、max と maxset を更新する end end printf("吸気系:%s\n排気系:%s\n馬 力 :%d\n",maxset[0],maxset[1], max) ⇒ work0401.rb ■ 宿題:演習4(d)のメインルーチンと演習4(d)の実行結果を印刷し、提出せよ。