人間なんて...ららら...

  1. 導入
  2. 準備
  3. プログラム作成
  4. 問題
  5. 付録

1. 導入

VisualWorksに、自分で新しいクラスを作成してみましょう。ここでは、人間(HumanBeing)というクラスを作り、名前と誕生日を指定してインスタンスを生成すると、満年齢・現在まで生きてきた日数・平均寿命までの残りの日数を答えられるようにします。


2. 準備

プログラムの作成は、システムブラウザで行います。VisualWorksランチャーに並んでいるアイコンの左から4番目にあるアイコンを押すと、ウィンドウが開きます。VisualWorksランチャーの「Browse」メニューで「System」を選択する、もしくはVisualWorksランチャーを選択している時に「F5」キーをたたいても同様にシステムブラウザが開きます。

Preparation_01_1.jpg Preparation_01_2.jpg

システムブラウザは、大きく上下に分かれていて、さらに上部は4つに分かれています。上部の左から、パッケージ(package)、クラス(class)、プロトコル(protocol)、メソッド(method)を表示するペイン(pane:小窓)です。下部は、基本的に上部で選んだもののソースコードが表示されます。

Preparation_02.jpg

パッケージとプロトコルは、それぞれの隣に表示されるクラスとメソッドをわかりやすいように束ねておくものです。


3. プログラム作成

まず、パッケージを作成しましょう。システムブラウザの「Package」メニューから「New Package...」を選択します。パッケージペインで右ボタンメニューを出し、「New Package...」を選んでも同様に、新パッケージ名を尋ねるダイアログが表示されます。

Programming_01_1.jpg Programming_01_2.jpg

新パッケージ名を入力するダイアログが開きますので、「Foo-Example」と入力し、「OK」ボタンを押しましょう。すると、システムブラウザ上部のパッケージペインに入力したパッケージが追加されます。

Programming_02_1.jpg Programming_02_2.jpg

次に、新しいクラス「HumanBeing」を作成します。パッケージペインで「Foo-Example」を選択し、クラスペインで右ボタンメニューを出して「New Class...」を選択すると、新クラスの情報を入力するダイアログが出ます。

Programming_03_1.jpg Programming_03_2.jpg

「Name」はクラス名、「Instance Variables」はインスタンス変数名です。ここでは、クラス名「HumanBeing」、インスタンス変数に「name」「birthday」を作成します。図のように、ダイアログの「Name」に「HumanBeing」を、「Instance Variables」に「name birthday」を入力します。インスタンス変数はスペースで区切ることで複数指定することができます。また、「Create methods」ではメソッドの自動生成を行えます。今回は、「Accessors」(インスタンス変数にアクセスする)、「Initializer」(初期化する)、「Subclass responsibilities」(子クラスで実装しなければならないメソッド)のすべてにチェックを入れた状態で「OK」ボタンを押しましょう。クラスが生成されると、クラスペインに「HumanBeing」クラスが、プロトコルペイン、メソッドペインに自動生成されたメソッドたちが表示されます。

Programming_04_1.jpg Programming_04_2.jpg

ではここから、各メソッドの編集を行います。まずは、自動生成されたメソッドを修正しましょう。

プロトコルペインでInstanceタブを選び、「initialize-release」→「initialize」を選択します。初期化をするメソッドの修正です。

Programming_05.jpg

コメント(ダブルクォートで括られた箇所)と、最後に「self」を返している箇所を削除します。

initialize

        name := nil.
        birthday := nil

編集しおわったら、フォーマット (プログラムの整形) をします。プログラムソースの枠内で、右ボタンメニューから「Format」を選択します。メソッド名が太字になり、ソースのインデント等がそろいます。

Programming_06_1.jpg Programming_06_2.jpg

そして、プログラムをアクセプトします。プログラムソースの枠内で、右ボタンメニューから、「Accept」を選択します。アクセプトすると、プログラムが再登録されます。

Programming_07.jpg

次に、プロトコルペインでClassタブを選び、「instance creation」→「new」を選択します。インスタンスを生成するメソッドの修正です。

Programming_08.jpg
new

        ^(super new)
                initialize;
                yourself

編集しおわったら、フォーマットアクセプトを必ずしましょう。

Programming_09.jpg

自動生成したメソッドは、引数がある場合その名前の意味がアバウトになっています。わかりやすくするために、引数名を変更しておきましょう。プロトコルペインでInstancesタブを選び、「accessing」→「birthday:」を選択します。

Programming_10.jpg

引数名が「anObject」になっていますが、誕生日は日付ですので、「aDate」に変更しましょう。

birthday: aDate

        birthday := aDate

くどいようですが、フォーマットアクセプトを忘れずに!

Programming_11.jpg

同様に、「name:」メソッドの引数も修正しましょう。名前ですから、必ず文字列を受け取りますね。よって、「anObject」を「aString」に変更します。

name: aString

        name := aString

では京都産業大学が創立されて何年経ったかを調べる例題プログラムを作成してみましょう。まず、プロトコルペインでClassタブを選び、「examples」というカテゴリを作成します。例題プログラムは、クラスメソッドとして定義しましょう。プロトコルペインで右ボタンメニューを出し、「New...」を選択すると、新カテゴリ名を入力するダイアログが開きます。

Programming_12_1.jpg Programming_12_2.jpg

ここでは「examples」というカテゴリ名で登録します。「OK」ボタンを押すと、プロトコルペインに「examples」が表示されます。

Programming_13_1.jpg Programming_13_2.jpg

「HumanBeing」クラスのインスタンスを生成し、名前に「京都産業大学」、誕生日に「1965/05/04」をセットして年齢(創立されてからの年数)を取得します。そして、名前、誕生日、年齢をトランスクリプトに出力するメソッド「example1」を作成しましょう。以下のようなプログラムになります。examplesカテゴリを選択し、下部のソースコードペインにコピー&ペーストしてください。

example1
        "HumanBeing example1."

        | aBeing |
        aBeing := HumanBeing new.
        aBeing name: '京都産業大学'.
        aBeing birthday: (Date newDay: 4 monthNumber: 5 year: 1965).
        Transcript
                clear;
                nextPutAll: aBeing name;
                cr;
                nextPutAll: aBeing birthday printString;
                cr;
                nextPutAll: aBeing age printString;
                flush.
        ^aBeing

2行目のコメントは、このメソッド自身を実行するプログラムです。こうしておくと便利ですよ。

フォーマットおよびアクセプトすると、メソッドペインにexample1が表示されます。

Programming_14_1.jpg Programming_14_2.jpg

では、コメントにある実行プログラムを選択し、右ボタンメニューから「Do it」してみましょう。エラーダイアログが表示されます。

Programming_15_1.jpg Programming_15_2.jpg

エラーには「Message not understood: #age」と書かれていますね。まだ「age」メソッドを作成していないので、「ageメソッドなんて知らないですよ」と言われているわけです。

では、年齢を計算する「age」メソッドを作成しましょう。インスタンスが持っている誕生日と実行した現在年月日を元に計算するので、今度はインスタンスメソッドになります。プロトコルペインで「Instance」タブを選び「accessing」を選択します。ソースペインに以下のプログラムをコピー&ペーストし、フォーマットおよびアクセプトをして登録してください。

age

        | today age birthdayInThisYear |
        today := Date today.
        age := today year - self birthday year.
        birthdayInThisYear := Date
                                newDay: self birthday dayOfMonth
                                monthNumber: self birthday monthIndex
                                year: today year.
        today < birthdayInThisYear ifTrue: [age := age - 1].
        ^age
Programming_16.jpg

まず、誕生年と現在年の差分を取得し「age」というテンポラリ変数にゲットします。また、今年の誕生日の年月日を取得し「birthdayInThisYear」にゲットします。今日の年月日「today」が「birthdayInThisYear」より小さければ、今年の誕生日をまだ迎えていないことになるので、「age」から1を引きます。これが年齢になるわけですね。読めるでしょうか?

では、先ほど作成したクラスメソッド「example1」をもう一度実行してみましょう。どうでしょうか。トランスクリプトに、名前「京都産業大学」、誕生日「May 4, 1965」、年齢(創立?年目)が出力されましたか?

次に、皆さんが今日まで何日生きてきたかを答えるプログラムを作ってみましょう。「example1」と同じようにクラスメソッドとして「example2」を作成します。以下のプログラムで名前と誕生日を自分のものに変えてみましょう。また、「age」と同じようにインスタンスメソッドとして「livingDays」を作成してください。

example2
        "HumanBeing example2."

        | aBeing |
        aBeing := HumanBeing new.
        aBeing name: '京都産業大学'.
        aBeing birthday: (Date newDay: 4 monthNumber: 5 year: 1965).
        Transcript
                clear;
                nextPutAll: aBeing name;
                cr;
                nextPutAll: aBeing birthday printString;
                cr;
                nextPutAll: aBeing age printString;
                cr;
                nextPutAll: aBeing livingDays printString;
                flush.
        ^aBeing
Programming_17.jpg
livingDays

        | days |
        days := Date today asDays - self birthday asDays.
        ^days
Programming_18.jpg

クラスメソッド「example2」を実行してみましょう。トランスクリプトに、名前、誕生日、年齢、今まで生きてきた日数が出力されると思います。10,000日くらいになりましたか?


4. 問題

平均寿命までの残り日数を得るプログラムを作りましょう。

日本における平均寿命を「男性が79歳」「女性が86歳」とします。すでにあるプログラムを利用して、自分の名前、誕生日、年齢、今まで生きてきた日数、平均寿命までの残りの日数をトランスクリプトに出力するプログラムを完成させてください。

以下のプログラムを実行すれば、上記のことを踏まえて、問題を解くための用意が調います。

| aString aURL aFilename aBrowser aWindow aPackage aNavigator aClass aProtocol aSelector |
aString := 'http://www.cc.kyoto-su.ac.jp/~atsushi/Programs/VisualWorks/Exercises/Exercise_HumanBeing/Exercise-HumanBeing.st'.
aURL := JunURL named: aString.
aURL exists ifFalse: [^nil].
aFilename := Filename defaultDirectory construct: aURL asURI tail.
aURL downloadTo: aFilename.
aFilename exists ifFalse: [^nil].
aFilename fileIn.
aBrowser := #{Refactory.Browser.RefactoringBrowser} value open.
aWindow := aBrowser builder ifNil: [^nil] ifNotNil: [:aBuilder | aBuilder window].
aWindow displayBox: (JunGeometry alignedBoxWithScreenCenter: 800 @ 700).
aPackage := Store.Registry packageNamed: 'Foo-Example'.
aPackage ifNil: [^nil].
aNavigator := aBrowser navigator.
aNavigator selectPundle: aPackage.
aClass := #{HumanBeing} value class.
aProtocol := #examples.
aSelector := #example3.
(aNavigator state)
        classesAndNameSpaces: (Array with: aClass);
        protocols: (Array with: aProtocol);
        selectors: (Array with: aSelector).
aNavigator
        setState: aNavigator state;
        changed
browser.png

5. 付録

Dateクラスのメソッドで、必要そうなものを紹介しておきます。詳細は「System Browser」を開いて調べてみましょう。

Date

■Classメソッド

■Instanceメソッド

プログラム例1:

age

        | today age birthdayInThisYear |
        today := Date today.
        age := today year - self birthday year.
        birthdayInThisYear := Date
                newDay: self birthday dayOfMonth
                monthNumber: self birthday monthIndex
                year: today year.
        today < birthdayInThisYear ifTrue: [age := age - 1].
        ^age

プログラム例2:

livingDays

        | days |
        days := Date today asDays - self birthday asDays.
        ^days

Updated: 2015/11/08 (Created: 2008/04/01) KSU AokiHanko