コンピュータ概論のページに、後期試験の日程をアナウンスしました。
試験の実施要項にあるように持ち込みは可です。
世の中では、こんなことも起きているようですが、この試験では、あまり意味がありませんよね..。
今日の演習では、lisp の資料の3章を参照して、「Lispで逆行列の計算」を実現します。
流れとしては、この資料の通り、実行すれば良いわけですが、その前に、「なぜ、このような流れで作成すればよいか」について説明しましょう。
Softwareを作成する(1)場合に、特に、大きなSoftware System(2)では、当然のことながら、いきなり、計算機に向かってProgramを作成するようなことはできません(3)。
少なくても次のようなステップ(4)が必要だということが解っています。
これから作成するProgramが持つべき機能や、制約等をまとめる作業。一般には、そのProgramの使用者の希望(5)を尋ね、「利用者が何を望んでいるか」を調べる段階です。
この作業の結果作成された資料を「要求仕様(あるいは、単に仕様)」、あるいは、Softwareを外からみた場合の振舞を記述していることが多いので、「外部仕様」と呼びます(6)。
与えられた(要求)仕様をどうやって実現するための方法を考える(7)段階です。
要求をどのような手法で解決するか?どのようなProgram単位に分割するか、また、どのような機能から実現し、どのように組み合わせるかなど、Softwareを作成するために、最も頭を使う所といえます。
アルゴリズムの選択や新規開発なども、この設計段階に含まれます。
この作業の結果作成される資料が、「設計書」あるいは「内部仕様」と呼ばれるものです。
コーディングという言葉は、先週も出て来た言葉ですが、ここではそれより狭く、「プログラムを作成する」という作業を指しています。
ここで重要なことは、「プログラムの機能」に関しては、原則として「設計」段階で、ほとんどの問題が解かれているので、ここでの作業は、「設計」で作られた内容(内部仕様)を、「プログラミング言語の形で表現(翻訳)する」というより単純な作業と考えられていることです。
もちろん、単純といっても、プログラミング言語に関する知識や、(内部)仕様を理解するような能力は要求されます。
試験は、作成されたプログラムが、確かに「(外部)仕様に沿っているものかどうかを判定する」段階です。
ここは、言ってみれば、サッカーに置けるゴールキーパーに相当している段階で、ここで、誤り(8)を発見できなければ、それがそのまま出荷され、利用者の所にそのまま届けられる(9)わけです。
なお、ここで誤りが発見された場合は、これの訂正を、ここでは行なわずに、前段階のコーディングや設計、最悪の場合は、要求分析まで、後戻りする(10)必要があります。
これは、既に利用を開始しているSoftwareに対して、後から手をいれることを意味しています。
これは、後から発見されたバグの修正( bug fix )や、機能の拡張や改良( version up )を指します。
その意味で、厳密には、「Software開発」のの一部ではないのですが、Software life cycleの一部としては、「生まれ変わり」としての「保守」の段階(これによって、新しい一生が始まる)が重要(11)であるという認識もなされています。
今回の演習では、さすがに、繰り返しや、保守までは議論できない(12)ので、開発の各段階、特に設計段階に注目して説明します。
演習で作成するProgramのように、数行で書けるProgramもちろん、今日作成する逆行列の計算Programも、全然、大きくありません。
本当に巨大なSoftwareというものは、数万行か、それ以上の量からなるProgramを指します。このような巨大なSystemでは、大勢の人間が数ヵ月から数年の日時を費して作成する必要があります。
もちろん、本当に「できない」わけではありません。だって、そうでしょう、別に計算機に向かうのはその人の自由なのですから..。
ただ、経験的に解っていることは、「そのようなことをすると、ほとんどの場合、後になって、とっても大変なことになる」ということが解っているというだけで、それが解っている人は、決して、いきなり、計算機の前に行ったりしないということだけです。
もちろん、皆さんがこの演習で学んでいるような規模の小さな(極小とも言える.. )サイズであれば、何の問題もなく、計算機の前に座っても特に問題はないのですが...。
一般に、Programの利用者と作成者は違います。「要求」というのは、利用者が持つもので、それを、製作者が正確に読み取る(聞き出す)ことが、この作業の大きな目的であり、更に、それを誰にでも解る形で、資料とする( Document化)ことも重要です。
例えば、「課題」が提出された場合、利用者は、「課題を出した教員」となり、製作者は、「問題を解く学生」となるわけです。課題を良く読み、利用者が何を狙っているのかを正確に汲み取らないと単位は貰えない..まあ、そうゆうことです。
一般に、このような「後戻り」が生じると、ほとんどの場合、それまで行なっていた作業が無駄になります。
したがって、このような「後戻り」は避けるべきだと言う議論(ウォーターフォールモデル)が昔ありました。
しかし、最近は、むしろ、「後戻りはある程度避けられない」、そこで、「後戻りが生じても、それほど無駄が生じないような工夫をして、できるだけ早い段階で、後戻りを覚悟しつつ、テスト段階まで進めた方が、結果的により早く(設計仕様、要求仕様の.. )誤りが発見できるので望ましい」という立場(繰り返し開発法)になってきました。
逆行列の要求(外部)仕様は、以下のものである。
この仕様で、機能の部分に関しては、特に問題はないと思われます。問題は、「制限」の所で、これは、「契約」に相当するため、後の設計の所で、考慮する必要があります。
この部分が焦点になります。
設計に於いては、次の観点が問題になります。
これは、次のデータ構造とも関係がありますが、このProgramに対して「どのような形で、入出力を行なうか?」を問題にします。
要求仕様上は、「行列の要素の値」とあるわけですが、「その行列をどのように表現するか?」等が問題になります。
また、上記の入力の制限に関してもここで、議論することになります。
ここでは、次のような「設計上の決定」を行ないます。
プログラムを作成する前に、データ構造を決定しておく必要があります。
ここでは、もちろん、「行列をどのように表現するか?」ということの他に、掃き出し法で、二つの行列を対にして扱うので、その「行列対」に関する仕様も確定する必要があります。
ここでの仕様上の決定は以下のようになります。
行列対は、二つの行列を要素と持つListで表現する。
また、その行列対に関しては、次のようなAccess関数を用意し、対に関しては、このacccess関数以外では操作しない(16)。
Programをできるだけ互いに独立した部分( Module )に分割し、それらの組合せで、全体が構成されるようにする。
全体の様子(外部・要求仕様)は解っているので、それを上手(17)に分割する方法を考え、実際に分割を行ないます。
この分割に関しては、後ほど、再び述べます。
このような仮定は、本来はかなり危険な仮定です。一般にこのような仮定を於いて、Programを作成すれば、大概、変な入力に対しては、変な動作しかしません。
入力というものは、普通何が来るか解らないので、頑丈(安全)なProgramを作成するには、入力に関する厳密なチェックが欠かせません。
しかし、(次の例にあるように..)一般に検査は、大変コスト(時間や労力)が掛かる上に、際限のないのが普通です。本当に何でもそれなりに対処するような検査を行なっても、時間が掛かるばかりです。
そこで、通常は、適当(←本当は「適切」と言いたいのだが.. )な所で切り上げるわけですが、そうすると、当然のことながら、将来に禍根を残すことになります。
そこで、登場するのが「契約」という考え方です。これは、「設計者に検査の要不要を指定する」仕組みです。
今回の場合、「入力は正方行列である」は、契約(要求仕様)の一部なので、「検査は不要」と決定して構わないわけです。
何かを作り上げる場合、それが、一目で解る程簡単な仕組みであれば、すぐに作成して構わないのですが、複雑なものであれば、一旦、それを分解して解りやすい形に置き換える必要があるでしょう。
このように「問題をいくつかの部分問題に分ける」と、一般に、「元の問題に比べ、個々の問題の解決を、個々の問題毎に行う方が容易である」という経験則があります。
この性質を利用して、問題を単純化するという方法を「分割統治法」と呼んでいます。
もちろん、一旦、分割したものを更に分割することを繰り返せば、それは、丁度、樹の根っ子のように、上から下に広がった関係ができます。
このような形で、Moduleを分割する手法を、「Top = Down設計」と呼びます。
このような「Top = Down設計」では、「何に着目して分割するか?」によって更に、分かれるわけですが、今回は、「機能に着目したTop = Down設計」(18)を行ないます。
これも、かなり昔から知られている手法ですが、ウォータフォールモデルと同様、現在は、余り注目されていません(が、良く利用はされています)。
やはり、小規模なSoftwareで、威力を発揮して来たのですが、大規模なSoftwareでは、力不足という感があります。
機能に着目することによって、全体の構成は、以下のような形に分割(19)されます。
mrev (逆行列の計算) | +-- 行列基本操作 ( nraw, nmelm, .. ) | +-- 単位行列の作成 ( nunit ) | +-- 対処理 | | | +-- 対作成 ( makepair ) | | | +-- 要素の取り出し ( pair1, pair2 ) | +-- 前進消去 (gfc) | | | +-- 正規化 (gfcnormal) | | | +-- 枢軸選び (gfcfind) | +-- 後退消去 (gbc)
Top=Downで設計されたプログラムは、普通、その逆順に作成することが多い。設計が、上から下方法なのに対して、コーディングは、下から上の方向に作成されるので、Bottom=Upと呼ばれる。
資料でも、ほぼ、このBottom=Up順に関数が作成されている。
固いウォーターフォールモデルでは、コーディングが全て済んでから試験をすることになっているのですが、すでに、これは古くなってしまいました。
現在は、少なくても、ここに繰り返し構造を導入します。
具体的には、「単体試験」と「結合試験」、そして「総合試験」というテスト段階の分離を行ないコーディングの段階の中に挿入します。
「単体試験」は、Moduleのコーディングが済んだ時点で、(他のModuleや、全体のコーディングの完了とは独立に.. )そのModuleだけの試験を行なうことです。
もし、この時点で誤りが発見された場合は、その時点で、逆戻りし、次の結合試験には進みません。
もし、全てModuleの単体試験が済めば、今度は、Moduleを組み合わせての試験、「結合試験」を行ないます。ここでも、Module間のInterfaceの食い違い(開発者の思い違いのことが多い)が発見されれば、どちらのModuleの問題かが明確化された上で、差し戻します。
そして、結合試験を繰り返し、全体として、一つのものに成った場合は、今度は、運用時の状況を想定しての試験「総合試験」が行なわれるわけです。
したがって、コーディング時と試験が交互にサンドイッチ状に実行されるわけです。
lispを参照して、先週に引き続き、3章の演習問題( [演習問題3.x.y]とある問題)を全て解いてみてください。
「xlispの演習問題(2章)の結果」をreportとして、送ってください。
提出内容は「xlispの2章の演習問題の結果」です。具体的には、xlispに対して、行った入力と、その結果をcopyしたものでOkeyです。例えば、演習1.1.1であれば、次のような形でOkeyです。
> (+ 123 456) 579 >
ただし、適当に空行を入れて、課題と課題の区別がつくようにしてください。
以前、紹介したsample.xmlを参考(20)に、XML文章を書きます。
なお、reportとして、提出するe-mailのSubjectには、必ず、[R]から始まる、わかり易いSubjectを付けてください。