以降の章では多くの段階をを要するプログラムの開発法を示します。僕らはプログラムとして何が必要か、何を無視すれば良いのか決めなくてはなりません。プログラムが何を用い、何を作り、どのように入出力が関連してるのか、知る必要があると言うことです。僕らが作っているプログラムに於いて、果たしてSchemeがデータを操作する基本機能を提供しているのか否か、も知らないといけません。もしそうじゃないのなら、それら補助機能を実装しないとならないでしょう。プログラムを作り上げた暁には、意図した動作をするかどうかもチェックしなければならないのです。それによって、シンタックスエラーや、実行時間の問題、あるいはロジックの間違いが明らかになるでしょう。
これらカオスを順序良く整理する為に、デザインレシピと言う方法を導入します。デザインレシピとは開発段階を示した規定の事で、要するに僕らがやらなければならない事を順序良く示したものです。今まで行なってきた事を見ても分かるでしょうが、プログラムを作る、と言う事は、最低でも、以下の4段階が必要になってきます。
;; Contract(契約): area-of-ring : number number -> number | |
;; Purpose(目的): ドーナツ型の面積を計算する | |
;; 外側の円の半径をouter、内側の穴の半径をinnerとする | |
;; Example(例): (area-of-ring 5 3) は 50.24 を返す | |
;; Definition(定義): [何かマシな事をここに書く] | |
(define (area-of-ring outer inner) | |
(- (area-of-disk outer) | |
(area-of-disk inner))) | |
;; Tests(テスト): | |
(area-of-ring 5 3) | |
;; expected value | |
50.24 |
プログラムの目的を理解する
プログラムのデザインのゴールは、データを加工し、加工済みのデータ返す機構を作り上げる事です。と言うことは、どんなプログラムの開発に於いても、どんな種類のデータを入れて、どんな種類のデータを出すのか、それらに対して明示的な名前を付ける事から始めないといけません。僕らはそれをCONTRACT
(契約)と呼んでいます。
一番最初に書いたプログラムのうちの一つ、area-of-ring
に対してのcontractは次のようなものです。
;; area-of-ring : number number -> number |
セミコロンはCOMMENT
を表しています。contractは二つの部分から成り立っています。左側の最初の部分はプログラムの名前です。右側にある二番目の部分は、どう言ったデータを用い、どう言うデータを返すのか、を表しています。そして、入力と出力は矢印で区切られています。
contractを作成した後はHEARDER
(ヘッダ)を作りましょう。そうすれば、プログラムの名前と入力の固有の名前を再確認する事が出来ます。それらの名前は(アルファベットでの)変数になり、プログラムのPARAMETERS
(引数)として参照されます。
訳注:ちょっとこの辺、ヘッダと言う言い方は一般的ではない。ここで示されてるヘッダと言うのは、本体を持たない関数の形式的テンプレートを意図しているらしい。例えば、以下で見るThis file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
(define (area-of-ring outer inner) ...)
を「ヘッダ」と呼んでいる。なお'...'は省略の意味を持っていなく、実際に'...'と書け、と言ってるのである。つまり、テストケースを最初に書くべきだ、と言うのがこの本(HtDP)の主張で、関数本体を記述するのは、ある程度のガイドラインを作成してから、と言う事である。
ちょっとarea-of-ring
のcontractとheaderを見てみましょうか。
;; area-of-ring : number number -> number | |
(define (area-of-ring outer inner) ...) |
これは最初の入力がouter
であり、次の入力がinner
である事を示しています。
最後に、contractと引数から、プログラムに対しての短いPURPOSE STATEMENT
(目的)を作る事が出来ます。PURPOSE STATEMENT
とは、プログラムが何を計算するのか、に付いて短く記述したものです。僕らがここで作るプログラムの殆どでは、一行か二行で充分でしょう。しかし、開発すべきプログラムが大きくなればなるほど、プログラムの目的に付いて詳細に記述する必要が出てきます。
以下に最初の例をあげておきます。
;; area-of-ring : number number -> number | |
;; ドーナツ型の面積を計算する | |
;; 外側の円の半径をouter、内側の穴の半径をinnerとする | |
(define (area-of-ring outer inner) ...) |
ヒント:もしも、問題文が数式を含んでたとしたら、式の独立変数の数がプログラムが必要な入力の個数を教えてくれるでしょう。
プログラムの例
プログラムが何を計算すべきかより良く知るには、入力例を考え、その出力が何になるのかを予想することです。例えば、area-of-ring
の場合、5と3と言う入力に対しては50.24と言う値を返す必要があります。と言うのも、外側の円の面積と内側の円の面積の差分が50.24だからです。
この例をpurpose statementに加えてみましょう。
;; area-of-ring : number number -> number | |
;; ドーナツ型の面積を計算する | |
;; 外側の円の半径をouter、内側の穴の半径をinnerとする | |
;; example(例): (area-of-ring 5 3) は 50.24 を返す | |
(define (area-of-ring outer inner) ...) |
プログラム本体を書き始める前に例を作る事はいろんな意味で有用です。まず、テストに於いて、ロジックのエラーを見つける唯一の正しい方法だからです。もし、プログラムを書き上げてから例を考えたりすれば、僕らはプログラムの方を信用しやすい。と言うのも、プログラムを走らせた方が、プログラムがどう言う結果を返すか予測するより遥かに簡単だから、です。二つ目に、後に行うより複雑な例をやれば分かるでしょうが、例がある事によって、より計算過程に対して慎重になる事ができ、関数本体を書く際に批評的な態度を持つ事が出来ます。最後に、例はpurpose statementをより具体化してくれます。将来的にプログラムを解読する人たち、例えば先生であるとか、同僚だとか、はたまたプログラムの購入者等は、抽象的なコンセプトを具体化して書いてくれている事に感謝してくれるでしょう。
本体
最後にプログラム本体を書きます。これは"..."を全てを置き換える事を意味します。Schemeの基本機能や、あるいは僕らが既に定義済みかあるいはこれから定義する機能を駆使して、引数から何を計算して答えとして返すのか全て表現していきます。
与えられた入力に従って、どのような出力を返すのか僕らが理解さえしていれば、プログラム本体はそれらを定式化するだけで済みます。仮に、入出力の関係が数式で定義されているのなら、僕らはそれをSchemeに翻訳するだけで済む。一方、文字関連の問題が与えられたなら、慎重に式を作成しないといけません。最終過程まで、第二段階で作成した例に戻る事は助けになるでしょう。そこで特定の入力に対してどう出力を計算すれば良いのか確かめられますから。
ここの例では、以前定義しましたが、明示されていないarea-of-disk
を使った計算を用いています。これがSchemeでの数式の翻訳になります。
(define (area-of-ring outer inner) | |
(- (area-of-disk outer) | |
(area-of-disk inner))) |
テスト
プログラムの定義を完了したあとでも、僕らは今度はプログラムをテストしないといけません。最低でも、プログラムの例から見て、正しい出力が出てくるか確認するべきです。テストを簡略化する為に、Difinitions
window(定義ウィンドウ)の一番下に、方程式のように例を書き加えておけばよいでしょう。その後、Execute
button(実行ボタン)をクリックすればそれらは評価されて、プログラムが正しく動いてるかどうか確認出来ます。
訳注:この本(HtDP)は前提としてPLT Scheme(現PLT Racket)を用いる事になっている。従ってDifinitions WindowやExecute buttonと言うのはPLT特有の表現であって、一般的なSchemeの話ではない。要するにこう言う事である。
PLT SchemeはIDE形式で提供されていて、上半分画面はDifinitions Window(定義ウィンドウ)と呼ばれている。Execute buttonは日本語ではそのまま実行ボタンとして表示されている。定義ウィンドウにSchemeコードを書いて、実行ボタンを押せば下のインタプリタに結果が出てくる。
テストでは、あらゆる入力に対して、正しい結果を返すかどうかは分かりません。と言うのも、通常、可能な入力と言うのは無限大にありうるからです。しかしながら、テストはシンタックスエラー、実行時間問題、ロジックのエラー等は明らかにしてくれるでしょう。
誤った出力に関しては、僕らは特にプログラムの例を見直してみないとなりません。場合によっては例自体が間違ってる事もある。と言う事は、プログラムがロジックのエラーを含んでるか、あるいは例もプログラムも間違っているか。どちらにせよ、その場合は最初から開発をやり直さないとならないでしょう。
- Contact(契約)とPurpose(目的)とHeader(ヘッダ)作成
- ゴール:関数の名前を付ける。入力データと出力データのクラス分け。目的を記述する。ヘッダを作成する。
- やるべき事:
- プログラムに問題に適した名前を付けよう。
- 関数が必要とする未知の事柄がいくつあるのか、そのヒントを求めて問題を研究しよう。
- 一つの入力に対して一つの変数を取り上げよう。可能なら、問題で与えられ、明示された名前を使う事。
- 選ばれた変数名を使って関数が何を作り出すのか説明しよう。
- contractを作成し、ヘッダを作ろう。This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
;; 関数名 : number ...--> number ;; x1から ... を計算して ... (define (name x1 ...) ...)
- やるべき事:
- ゴール:関数の名前を付ける。入力データと出力データのクラス分け。目的を記述する。ヘッダを作成する。
- 例の作成
- ゴール:例を通して入出力の関係を明らかにする。
- やるべき事
- 問題から例に適切なものを探そう。
- 良い例を心がけよう。
- 可能なら、結果を良く確認しよう。
- 例を作り上げよう。
- やるべき事
- ゴール:例を通して入出力の関係を明らかにする。
- 本体作成
- ゴール:関数を定義する
- やるべき事
- どのように関数が結果を計算するか定式化しよう。
- Schemeの基本関数やその他の関数と変数を駆使してSchemeの式を開発しよう。
- 可能な場合には、問題の数式表記を翻訳しよう。
- やるべき事
- ゴール:関数を定義する
- テスト作成
- ゴール:ミス(タイポやロジック)を見つける
- やるべき事
- 例の入力を関数に適用しよう。
- 出力が予測したものと同じかどうか確かめよう
- やるべき事
- ゴール:ミス(タイポやロジック)を見つける
デザインレシピはプログラムをデザインする過程において遭遇する、様々な問題を打ち倒す魔法の弾丸ではありません。デザインレシピとは開発中にしばしば起こりえる不可避なトラブルに対するある種のガイドラインなのです。レシピでもっともクリエイティブで、もっとも難しい部分は関数本体をデザインする事でしょう。この点は、参考書籍を読解する能力、数学的関係を明らかにする能力、基本的な事柄に対する知識に依存します。これらはコンピュータプログラムをする上に於いてはどれも必須ではありませんが、一方、作るべきアプリケーションによっては、専門的な知識が必要になる事がありえます。この本の残りでは、何を、どのようにして、これらの複雑な過程に対処していくかお見せしようと思っています。
専門知識:しばしば、問題によっては、その範疇での特殊な知識を用いて関数本体を仕上げなければならない事もあります。これらの知識を専門知識と呼びます。例えば、代数のような簡単な数学から微分方程式のような複雑な数学、あるいは、音楽、生物学、土木工学、美術等等等の非数学的な素養も必要になるかもしれません。
プログラマはあらゆる分野にわたるアプリケーションの全てを知ることは不可能なので、各種専門家と問題に付いて話し合う事が出来るように、それら多方面のアプリケーション分野で使われる用語を理解出来るように努めなければなりません。それらの用語はしばしば数学でしょうが、場合によっては、特にデータ関係のアプリケーション領域ですが、自分で用語を発明しなければならない事もあります。そういう背景もあって、プログラマは可能な限り、コンピュータ用語のしっかりした理解が求められているのです。
0 件のコメント:
コメントを投稿