Powered by SmartDoc

コンピュータ概論(2002/12/04)
Ver. 1.0

2002年12月4日
栗野 俊一
kurino@math.cst.nihon-u.ac.jp
http://edu-gw2.math.cst.nihon-u.ac.jp/~kurino/comp/index.html
コンピュータ概論2002/12/04 の資料

目次

お知らせ

演習

Software 作成の方針

今日の演習では、lisp の資料の3章を参照して、「Lispで逆行列の計算」を実現します。

流れとしては、この資料の通り、実行すれば良いわけですが、その前に、「なぜ、このような流れで作成すればよいか」について説明しましょう。

Software 作成の段階

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)ので、開発の各段階、特に設計段階に注目して説明します。

  1. SoftwareとProgramというのは、結構紛らわしい用語ですが、単純に言えば、「Softwareは、利用者が利用するような大きなまとまりのあるProgram群」の事であり、「Programは、Softwareの製作者が、Softwareの部分として、組み合わせ可能部品の単位としてとらえたもの」と考えるとよいでしょう。
  2. 演習で作成するProgramのように、数行で書けるProgramもちろん、今日作成する逆行列の計算Programも、全然、大きくありません。

    本当に巨大なSoftwareというものは、数万行か、それ以上の量からなるProgramを指します。このような巨大なSystemでは、大勢の人間が数ヵ月から数年の日時を費して作成する必要があります。

  3. もちろん、本当に「できない」わけではありません。だって、そうでしょう、別に計算機に向かうのはその人の自由なのですから..。

    ただ、経験的に解っていることは、「そのようなことをすると、ほとんどの場合、後になって、とっても大変なことになる」ということが解っているというだけで、それが解っている人は、決して、いきなり、計算機の前に行ったりしないということだけです。

    もちろん、皆さんがこの演習で学んでいるような規模の小さな(極小とも言える.. )サイズであれば、何の問題もなく、計算機の前に座っても特に問題はないのですが...。

  4. これを人間の一生に比して、「ソフトウェアライフサイクル」と呼んでいます。
  5. 一般に、Programの利用者と作成者は違います。「要求」というのは、利用者が持つもので、それを、製作者が正確に読み取る(聞き出す)ことが、この作業の大きな目的であり、更に、それを誰にでも解る形で、資料とする( Document化)ことも重要です。

    例えば、「課題」が提出された場合、利用者は、「課題を出した教員」となり、製作者は、「問題を解く学生」となるわけです。課題を良く読み、利用者が何を狙っているのかを正確に汲み取らないと単位は貰えない..まあ、そうゆうことです。

  6. 別の観点からいえば、これは「What is (これは何か?)」を表していると考えることもできます。
  7. したがって、ここでの仕様は「How to (いかにして為すか?)」を表していると考えることができます。
  8. Softwareの誤りをバグ( bug :虫のこと.. )と呼びます。これは、単に振舞が変というだけでなく、「仕様に沿っていない振舞」も「誤り」と解釈されます。
  9. これは、答案の見直しをせずに、提出するのと変わりません。そして、もちろん、誤りが教員の手で発見されれば、それば×になるわけで..。
  10. 一般に、このような「後戻り」が生じると、ほとんどの場合、それまで行なっていた作業が無駄になります。

    したがって、このような「後戻り」は避けるべきだと言う議論(ウォーターフォールモデル)が昔ありました。

    しかし、最近は、むしろ、「後戻りはある程度避けられない」、そこで、「後戻りが生じても、それほど無駄が生じないような工夫をして、できるだけ早い段階で、後戻りを覚悟しつつ、テスト段階まで進めた方が、結果的により早く(設計仕様、要求仕様の.. )誤りが発見できるので望ましい」という立場(繰り返し開発法)になってきました。

  11. 現在の「Softwareの開発」のほとんどが、実は、「新規開発」ではなく、「既存Softwareの改良(という名のbug fix )」であることが知られています( cf. windows update !! )。
  12. 時間も短いということも言えますが、そもそも、逆行列の計算という問題そのものが小さいので、「逆戻りは不要`ということです。

要求仕様

逆行列の要求(外部)仕様は、以下のものである。

機能
逆行列の計算
入力
逆行列を求めたい行列の要素の値
出力
引数で与えられた行列の逆行列の要素の値
制限等
入力に関しては次のような制約がある。
  1. 入力される行列は必ずしも正則とは限らない(逆行列が存在しないかもしれない)。
  2. 入力は、正方行列だけを扱う。

この仕様で、機能の部分に関しては、特に問題はないと思われます。問題は、「制限」の所で、これは、「契約」に相当するため、後の設計の所で、考慮する必要があります。

設計

この部分が焦点になります。

設計に於いては、次の観点が問題になります。

Interface (入出力)に関する設計

これは、次のデータ構造とも関係がありますが、このProgramに対して「どのような形で、入出力を行なうか?」を問題にします。

要求仕様上は、「行列の要素の値」とあるわけですが、「その行列をどのように表現するか?」等が問題になります。

また、上記の入力の制限に関してもここで、議論することになります。

ここでは、次のような「設計上の決定」を行ないます。

  1. 入力する行列の値は、次のデータ構造で指定した形で与える。
  2. 行列の次元は、不確定なので、そのサイズも引数でしてもらう(13)
  3. 入力された行列は正方行列であると仮定し、そのチェックは行なわない(14)
  4. 入力は、正則行列という保証はないので、その検査は行なう。
  5. 検査の結果、正則でない行列が与えれた(ようするに、逆行列がない.. )場合、この関数は値としてNILを返す(15)
データ仕様に関する設計

プログラムを作成する前に、データ構造を決定しておく必要があります。

ここでは、もちろん、「行列をどのように表現するか?」ということの他に、掃き出し法で、二つの行列を対にして扱うので、その「行列対」に関する仕様も確定する必要があります。

ここでの仕様上の決定は以下のようになります。

  1. 行列は、行ベクトルのListで表現する。また、行ベクトルは、要素のListで表現する。
  2. 行列対は、二つの行列を要素と持つListで表現する。

    また、その行列対に関しては、次のようなAccess関数を用意し、対に関しては、このacccess関数以外では操作しない(16)

    1. 二つの要素を与えて、対を作る
    2. 対の一つ目の要素を取り出す。
    3. 対の二つ目の要素を取り出す。
プログラム設計
データ構造の次はプログラムの設計が必要です。プログラムの設計には単純に次の二つの側面があります。
アルゴリズム
動作を記述するための手順を考えるものです。今回の場合は、「掃き出し法」がそれに相当します。
Module分解

Programをできるだけ互いに独立した部分( Module )に分割し、それらの組合せで、全体が構成されるようにする。

全体の様子(外部・要求仕様)は解っているので、それを上手(17)に分割する方法を考え、実際に分割を行ないます。

この分割に関しては、後ほど、再び述べます。

  1. これは、プログラムを簡単にするためなので、実は、実装者( Program作成者)の都合です。普通は、このような実装者側の都合を設計仕様として、導入することはありませんが、まあ、「設計時の決定例」と考えてください。
  2. このような仮定は、本来はかなり危険な仮定です。一般にこのような仮定を於いて、Programを作成すれば、大概、変な入力に対しては、変な動作しかしません。

    入力というものは、普通何が来るか解らないので、頑丈(安全)なProgramを作成するには、入力に関する厳密なチェックが欠かせません。

    しかし、(次の例にあるように..)一般に検査は、大変コスト(時間や労力)が掛かる上に、際限のないのが普通です。本当に何でもそれなりに対処するような検査を行なっても、時間が掛かるばかりです。

    そこで、通常は、適当(←本当は「適切」と言いたいのだが.. )な所で切り上げるわけですが、そうすると、当然のことながら、将来に禍根を残すことになります。

    そこで、登場するのが「契約」という考え方です。これは、「設計者に検査の要不要を指定する」仕組みです。

    今回の場合、「入力は正方行列である」は、契約(要求仕様)の一部なので、「検査は不要」と決定して構わないわけです。

  3. 例外が生じた場合に、どのように扱うかも、Interfaceの大事な側面です。
  4. このように「あるものデータ表現する」場合、その表現方法と同時に、それにaccessするための関数群を与え、それでしかaccessしないように設計することをObject Oriented (オブジェクト指向な)設計と呼びます。
  5. 下手に分割すると、却って変な事になります。ここらへんは、設計者の手椀の問われる所ですが、ある程度、機械的に行なうための方策もいろいろと提案されています。

Module 分解の方法

何かを作り上げる場合、それが、一目で解る程簡単な仕組みであれば、すぐに作成して構わないのですが、複雑なものであれば、一旦、それを分解して解りやすい形に置き換える必要があるでしょう。

このように「問題をいくつかの部分問題に分ける」と、一般に、「元の問題に比べ、個々の問題の解決を、個々の問題毎に行う方が容易である」という経験則があります。

この性質を利用して、問題を単純化するという方法を「分割統治法」と呼んでいます。

もちろん、一旦、分割したものを更に分割することを繰り返せば、それは、丁度、樹の根っ子のように、上から下に広がった関係ができます。

このような形で、Moduleを分割する手法を、「Top = Down設計」と呼びます。

このような「Top = Down設計」では、「何に着目して分割するか?」によって更に、分かれるわけですが、今回は、「機能に着目したTop = Down設計」(18)を行ないます。

  1. これも、かなり昔から知られている手法ですが、ウォータフォールモデルと同様、現在は、余り注目されていません(が、良く利用はされています)。

    やはり、小規模なSoftwareで、威力を発揮して来たのですが、大規模なSoftwareでは、力不足という感があります。

Module 分解

機能に着目することによって、全体の構成は、以下のような形に分割(19)されます。

       mrev (逆行列の計算)
         |
         +-- 行列基本操作 ( nraw, nmelm, .. )
         |
         +-- 単位行列の作成 ( nunit )
         |
         +-- 対処理
         |     |
         |     +-- 対作成 ( makepair )
         |     |
         |     +-- 要素の取り出し ( pair1, pair2 )
         |
         +-- 前進消去 (gfc)
         |     |
         |     +-- 正規化 (gfcnormal)
         |           |
         |           +-- 枢軸選び (gfcfind)
         |
         +-- 後退消去 (gbc)

  1. これらの分割された個々の部分(これをModuleと呼ぶ)で、最初の二つは、今回のmrev以外にも利用可能な「汎用Module」である。これらは、他にすでに( Libraryの形で.. )あれば、それを利用するし、逆に無ければ、他からも利用できる形( Library )にする。

コーディング (Bottom=Up 作成)

Top=Downで設計されたプログラムは、普通、その逆順に作成することが多い。設計が、上から下方法なのに対して、コーディングは、下から上の方向に作成されるので、Bottom=Upと呼ばれる。

資料でも、ほぼ、このBottom=Up順に関数が作成されている。

試験

固いウォーターフォールモデルでは、コーディングが全て済んでから試験をすることになっているのですが、すでに、これは古くなってしまいました。

現在は、少なくても、ここに繰り返し構造を導入します。

具体的には、「単体試験」と「結合試験」、そして「総合試験」というテスト段階の分離を行ないコーディングの段階の中に挿入します。

「単体試験」は、Moduleのコーディングが済んだ時点で、(他のModuleや、全体のコーディングの完了とは独立に.. )そのModuleだけの試験を行なうことです。

もし、この時点で誤りが発見された場合は、その時点で、逆戻りし、次の結合試験には進みません。

もし、全てModuleの単体試験が済めば、今度は、Moduleを組み合わせての試験、「結合試験」を行ないます。ここでも、Module間のInterfaceの食い違い(開発者の思い違いのことが多い)が発見されれば、どちらのModuleの問題かが明確化された上で、差し戻します。

そして、結合試験を繰り返し、全体として、一つのものに成った場合は、今度は、運用時の状況を想定しての試験「総合試験」が行なわれるわけです。

したがって、コーディング時と試験が交互にサンドイッチ状に実行されるわけです。

xlisp の演習 ( 3 章 )

lispを参照して、先週に引き続き、3章の演習問題( [演習問題3.x.y]とある問題)を全て解いてみてください。

課題提出

「xlispの演習問題(2章)の結果」をreportとして、送ってください。

内容

提出内容は「xlispの2章の演習問題の結果」です。具体的には、xlispに対して、行った入力と、その結果をcopyしたものでOkeyです。例えば、演習1.1.1であれば、次のような形でOkeyです。

> (+ 123 456)
579
>

ただし、適当に空行を入れて、課題と課題の区別がつくようにしてください。

形式

以前、紹介したsample.xmlを参考(20)に、XML文章を書きます。

<author>の所
当然ながら、自分の名前、学生番号学科を書く
<id>の所
20021204001
<title>の所
lisp演習(2)
<date>の所
2002/12/04
<body>の<contents>の場所
上記のxlispとのやりとりのcopy

なお、reportとして、提出するe-mailのSubjectには、必ず、[R]から始まる、わかり易いSubjectを付けてください。

提出方法
提出は、e-mailで行いますが、次の点に注意してください。
宛先
math-comp-examine@media.cst.nihon-u.ac.jp
表題( Subject )
[R]から始まる、わかり易いSubject (例「[R] lisp課題2」など)
内容
課題のXML文章そのもの(送付書類は不可)
電子署名
clear署名を付けること。なお、その公開鍵は、Webを登録するのに用いるものと同じものとする。
  1. このテキストを利用する時には、[ファイル]→[名前を付けて保存]とするか、[表示]→[ソース]としてから、マウスでカットアンドペーストしてください。なにもせずに、そのままマウスでカットアンドペーストすると変なものになります。