ここでは、 構造化プログラミング と 構造化定理 の違いなどを説明した後に世界で最初に「構造化プログラミング(Structured programming)」という言葉を用いた文章 "7.4 STRUCTURED PROGRAMMING"(1969年) の翻訳を掲載します。
NATOソフトウェア工学会議によると、この文章は論文(Paper)ではなくて、研究成果報告書(Working paper)ということになっています。研究成果報告書は、研究の概要や成果を書いたものです。
"7.4 STRUCTURED PROGRAMMING" を書いたダイクストラ博士は、1972年に他の科学者と共同で "Structured programming" という本を出版します。これが構造化プログラミングのバイブルになっています。
筆者がこのページを作成しようと思った動機は、ネット上に「CASIO BASIC は構造化プログラミング可能」という誤りを力説する人がいたからです。
その人は、構造化定理のことを構造化プログラミングだと思っているようです。
しかし、その人に限らず、構造化プログラミングと構造化定理を混同している人は非常に多いのです。
以下の表を見て下さい。
名称 | 構造化プログラミング(Structured Programming) | 構造化定理(Structure Theorem) |
考案者 | エドガー・ダイクストラ(Edsger Wybe Dijkstra) | ハーラン・ミルズ(Harlan Mills) |
発表年 | 1969年 | 1970年代初頭 |
目的 | 大きなプログラムを書いた時点でプログラムの正しさを証明する。 | プログラムから goto文をできるだけ排除する。 |
手段 |
・段階的抽象化(ボトムアップで抽象化)
・段階的詳細化(トップダウンで詳細化) ・抽象化データ構造とその上で動作する抽象化文との共同詳細化(後のオブジェクト指向のクラスに通じる) ・プログラムの階層化 など |
プログラムを「順次」「反復」「分岐」によって記述し、goto文をできるだけ排除する。 |
多くの人が構造化定理のことを構造化プログラミングと混同していますが、上の表のように完全に異なるものです。
ただし、構造化プログラミングの一部として、構造化定理と同様の「goto文よりもできるだけ順次・反復・分岐(+手続き呼出)を使う」が含まれているのは確かです。 しかし、構造化プログラミングにとっては、一部の要素にすぎません(段階的抽象化のために必要とされているだけ)。 構造化プログラミングの目的は「大きなプログラムを書いた時点でプログラムの正しさを証明する」であり、goto文の排除はそのための一部の手段にすぎないのです。
一方、構造化定理は、1966年に発表された 'Böhm-Jacopini theorem'(ベーム-ヤコピーニの定理)というフローチャートのための定理が元になっており、構造化プログラミングの影響を受けて、ハーラン・ミルズがそれを焼き直したものです。 その目的は「プログラムから goto 文をできるだけ排除する」という非常に単純なものです。 「能力の低いプログラマーでも goto文を使わなければ、少しはマシなプログラムが書ける」という程度のことに過ぎません。
しかし、構造化プログラミングと構造化定理が混同されてしまったのです。 名前が似ている(英語でも似ている)上に構造化定理の方が簡単で効果もそれなりにあったからでしょうか。
さらに構造化プログラミングが未完成な理論だったというのもあるでしょう。
その目的が
「大きなプログラムを書いた時点でプログラムの正しさを証明する」
という途方もないものです。
おそらく、その目的は実現不可能だったでしょう。
しかし、構造化プログラミングが提案した手段(上表参照)は今では当然のこととなっており、その実質的なソフトウェア工学への影響は構造化定理をはるかに越えていると言えるでしょう。
ここまで読めば、「CASIO BASIC は構造化プログラミング可能」と力説する人がおかしいことに気がつくでしょう。 CASIO BASIC は、プログラムの階層化ができません。抽象化データ構造も作れません。 構造化プログラミングを実現するには、少なくともC言語程度の機能が必要です(できれば、オブジェクト指向言語を使うべき)。
ただし、CASIO BASIC は、構造化定理を満たしているかもしれません。 しかし、CASIO BASIC は、過去機種との互換性を保つせいか goto文(あるいは、それに類する命令)を積極的に排除していません。
下記リンクの書類の "7.4 STRUCTURED PROGRAMMING"(P65〜P68)が、ダイクストラ博士の構造化プログラミングの研究成果報告書(Working paper)です。
ダイクストラ博士はこの文章で初めて「構造化プログラミング(Structured programming)」という言葉を使いました。
このPDF書類は、1969年8月27日〜8月31日にイタリアのローマで開催されたNATOソフトウェア工学会議(The NATO Software Engineering Conferences)のレポートです。 この会議を主催したのは、NATO科学委員会(NATO SCIENCE COMMITTEE)であり、現在は NATO Science for Peace and Security(平和と安全のためのNATOの科学)と合流して消滅しています。
上のPDF書類は原典をテキストデータ化してページ配置を変更したものです。 それなのに目次(CONTENTS)のページ番号が修正されていないので、目次のページ番号と実際のページ番号がずれてしまっています。 そのため、本文中に 85 (背景が黒の数値)のような数値が印刷されており、それが原典のページ番号を示しています。 さらにこの書類の2ページ目に書かれているようにOCRで読み取ったものを修正したものなので、多少の誤字が含まれている可能性があります。
ネット上に何故か翻訳された文章もないようなので、自分で翻訳しました。すでに同文章の翻訳があれば申し訳ございません。 筆者の英語力で翻訳するには難しい文章でした。翻訳が下手だと思われた時はご容赦ください。 原文を尊重して意訳は最小限にしています。ほぼ直訳なので、分かり難いところは [1-1] のような訳注を付けています。
多少の誤訳もあるでしょうが、構造化プログラミングが構造化定理とは完全な別物ということは理解できるでしょう。
(以下の翻訳文を読むにはコンピューター工学の知識が必要です)
この研究成果報告書(Working paper)は、去年著者が行ったプログラミング実験の中で得られた経験と洞察について報告する。 主な疑問は、我々のプログラミング能力を一桁増やすことが可能かどうか。そして、そのためにどのような技法(精神的、組織的または機械的な技法)をプログラム構築の過程において適用できるのかということであった。 そのプログラミング実験はこれらの疑問に光を当てるために行われた。
私が心から心配していることは、本質的に大きいプログラムに存在している。 「本質的に大きい」とは、プログラムがその課題の複雑さゆえに大きくなっていることを意味しており、爆発して大きくなったプログラム(不適切な知識、不幸な決断、問題の不十分な理解、その他による)とは対照的である。 事実、現実的な理由から、私の実験は今までのところかなり小さなプログラムで実行しなければならなかった。 そのことが深刻な難しさを示していた。 私は明確に大きさの問題を扱うこと、そして(未だに非常に高価な [1-1] )実験よりもむしろ分析・観察・熟考によってできるだけ多くの結果を見つけ出そうとすることによって、それを克服しようと試みてきた。
そうすることで、どうやら実現するために学ぶ必要がある多くの下位目標 [1-2] を発見した(やり方を知らない下位目標であれば、我々は学ぶ必要がある)。
もしもある大きなプログラムが N 個の「プログラム部品」から構成されており、N が非常に大きいならば、個々の部品の信頼性レベルは例外的に高い必要がある。もしも個々の部品が正しく動作する確率 "p" で表現できるならば、大きな N に対してプログラム全体の機能が正しく動作する確率は
を超えることは厳密にはありえない。P が 0 よりもはるかに大きいならば、p は 1 にほぼ等しくなければならない [1-4]。 部品を組み合わせてより大きな部品を組み立てて、大きな部品からプログラム全体が構成されていても何の解決にもならない。
結果として、プログラムの正しさ(信用できるレベル)の問題は、私の主な懸念の1つであった。
十分説得力のある方法でプログラムの正しさを証明するために必要とされる労力(それが知的にせよ実験的にせよ)は、プログラムの長さ(均等かつ大雑把に測定される [1-6])に比例するよりも速く増大することはないのかもしれない(労力の増大速度は多少大雑把に測定される [1-7])。 もしも、例えば、N 個のプログラムの部品(それら1つ1つは正しいと仮定される)からなるプログラム全体の正しい構成の検証に関わっている労働者が N に従って指数的にさらに増大するならば、我々は敗北を認めた方がいい。 あらゆる大きなプログラムは多くの異なるバージョンの中のそのバージョンの存続期間の間に存在する。 その結果、大きなプログラムを構成する場合、単一のプログラムに関心をそれほど持つことはない。 しかし、関連するプログラムの全集団に関心を持たないといけない。全集団には、同じ課題のための代わりになるプログラムや類似の課題のための類似のプログラムを含んでいる。 それゆえに1つのプログラムは全集団の1つのメンバーとして考えられ、理解されるべきである [1-8]。 プログラムは部品から構築されるべきなので、この集団の様々なメンバーは部品を共有し、共有された部品の正しさの証明を共有するだけでなく、共有された下位構造の正しさの証明も共有することになる [1-9]。
プログラムの正しさの成立条件 [2-1] は、このプログラムによって引き起こされるかもしれない計算処理の正味の影響についての成立条件である。 そのような成立条件がどのようにすれば正しいとすることができるのかを検証するために、私は以下の結論に至った。
実行可能な計算処理(時とともに進化する [3-1])についての成立条件がどのようにして静的なプログラムテキストから作られるのかを調査したところ、様々なプログラムの動作手順からの段階的抽象化 [3-2] を可能にするために厳格で順序付けられた規律に忠実であることが重要であると私は結論づけた。 特に逐次処理コンピューターのためのプログラムがコンピューター言語の基本的な記号の直線的な連続 [3-3] として表現されるとき、ラベルの付いた箇所への転送制御文 [3-4] よりもむしろ、実行順序は二者択一的、条件的、そして反復的な節と手続き呼び出し [3-5] によって制御されるべきである。
局所的な順序 [3-6] からの段階的抽象化のために必要なことは、以下の実例によっておそらく最も確信的に示される。
形式(1)の「展開された(stretched)」プログラムを考えてみよう。
そして、個々の文 S j を実行した正味の影響が与えられているとき、プログラム(1)の正しさを立証する(すなわち、連続した N の行動の正味の影響の累計が、プログラム(1)によって実行される計算処理に課された要求を満たすということを立証する)ことが理論上 N の手順を必要とするという測定の決まり事を導入しよう。
形式(2)の文について、
ここで再び、構成する文 S 1 と S 2 の実行による正味の影響が与えられているとする。 プログラム(2)の正味の影響を立証することが、理論上2つの手順を必要とするという測定の決まり事を導入する。 換言すると、Bが真の場合とBが偽の場合があるということである。
さて、形式(3)のプログラムを考えてみよう。
二者択一の文(if文)を理解するために二者択一の文毎に2つの手順を必要とするという測定の決まり事に従う [3-7]。 すなわち、以下の文の正味の影響を立証するということである。
この文の正味の影響は、抽象化文 S 1 を実行したときの正味の影響と等価である [3-8]。 このような二者択一の文が N あるとすると、プログラム(3)をプログラム(1)の形式に縮小するために 2N の手順を必要とする。 プログラム(1)を理解するために N の手順が必要であり、全部で 3N の手順が必要である [3-9]。
もしも我々が抽象化文 S i の導入を拒否し、文 S ij の実行を使ってプログラム(3)を直接的に理解しようとしたならば、プログラム(3)のような計算処理は、S ij のような文を N 実行したときの累計の影響であり、それを理解するためにそれ自体が N の手順を要求するだろう。 Sij を使ってアルゴリズムを理解しようと試みることは、プログラム(3)全体を通して 2 N [3-8] の異なる経路決定を区別しなければならないことを暗示する。そして、理論上 N*2 N [3-9] の手順をもたらすのだ! [3-10]
私は以上の計算が抽象化文 S i の導入の必要性を説得力をもって実証していると信じている。 私の構成手法のある1つの特徴は、与えられたプログラム(3)を抽象化プログラム(1)へ縮小することではなく、抽象化プログラム(1)から始めることである [3-11]。
もしもプログラムを構成する文の正味の影響の定義が十分扱い難い [4-1] ものであれば、適当な数の抽象化文から構成されているプログラムを再び理解することは、莫大な作業になる。 このことは適切な 抽象化データ構造 の導入によって克服することができる。 その状況は、使用されている実装上の数値表現について考えずに整数における ALGOL プログラムの動作を理解することができる [4-2] ことと非常に類似している。 唯一の違いは、プログラマーが自分自身で概念(「既製」の整数と類似)とその概念上の動作(「既製」の数値演算と類似)を考案することである。
抽象化プログラム(すなわち、 抽象化データ構造上で動作する抽象化文 から構成されている)の詳細化において、我々は 「共同詳細化」 という現象を観察することになる。 定められた型の抽象化データ構造に対して信頼できる表現は、新しいデータ構造(おそらく未だにかなり抽象的ではあるが)を使って決められる [4-3]。 この設計決定の直接の結論は、「新しいデータ構造が独自の抽象化データ構造を表現するために決定されたという観点から、独自の抽象化データ上で動作する抽象化文は、新しいデータ構造上で機能するアルゴリズム詳細化を使って再定義されなければならない」ということである [4-4]。 そのようなデータ構造と関連する文との共同詳細化 [4-5] は、プログラムテキストの分離した1単位であるべきである。それは(自立した)設計決定の直接の結論を具体化したものであり、それ自体がプログラム変更のために交換できる自然な一単位である。 それは私が「真珠」と呼ぶまでに育てたものの一例である。
私は真珠の整然とした集合である「ネックレス」[5-1] としてプログラムをみなすまでに成長した。 最も上にある真珠は最も抽象的な形式でプログラムを記述する。それより下の全ての真珠において、それらの真珠の上で使われた1つ以上の概念は、それより下の真珠で説明される(詳細化される)という概念を使って説明される(詳細化される)。 一方、最も下にある真珠はついに標準インターフェース(=機械)[5-2] を使って説明されなければならないことを説明している。 その真珠は自然な単位のプログラムモジュールであるように見える。
それぞれの真珠は特定の設計決定(または、場合によっては、元の課題の特定の一面)を具体化しているので、それはプログラムの変更(または、場合によっては、課題の変更に対するプログラムの適応)において交換できる自然な単位である。
真珠とネックレスは、ネックレスの上半分から構成される「不完全なプログラム」に明確な状態を与える。 適切な機械 [5-3] (ネックレスの下半分が適切な機械の実行可能な実装を提供する)によって実行されることは、完全なプログラムと見なすことができる。 そのようなものとして、ネックレスの上半分の正しさは、下半分の選択に関わらずに確立されることができる。
2つの繋がった真珠の間を切って切口を作ることができる。その切口は、切口より下のネックレスの部分によって提供される機械の説明書 [5-4] であり、切口よりも上のネックレスの部分によって表現されるプログラムによって使用される。 この説明書はネックレスの2つの部分の間のインターフェースとして役立つ。 この形式のインターフェースは命令と命令の間のインターフェースとしてのデータの表現 [5-5] を考えるよりも役立つと思われる。 特にプログラムの適応のために要求される組合せの自由を確保するためにさらに役に立つ。
今述べた組合せの自由は、関わっている労働者をソフトウェア集団のメンバーの数に比例して増やさずにソフトウェア集団の一部としての一つのプログラム、あるいは「多くの(潜在的な)バージョンの中の」一つのプログラムを作成することができる唯一の方法に思える。 そのソフトウェア集団は、ヒモを通してネックレスにすることができる真珠のコレクションの中から選んだ真珠で作ったネックレスの集合になる [5-6]。
ネックレスの中の真珠は、「最上部から底辺まで」と言う厳密な論理的順序を持つ。 この順序は、それらが設計された(時間的)順序とは根本的に異なるかもしれないことを強調させていただきたい。
関連するプログラムのある分類の大量のメンバーをできるだけ完全に相互に対応付けしようとしたとき [6-1] 、真珠がプログラムのモジュールとしての存在であることが明らかになってきた。 この対応付けに関わる抽象化作業 [6-2] は、正しさの証明に従事する知的労働者の数を減らすために使われること [6-3] と同じことであることが判明する [6-4](結果論としては驚くべきことではない!)。 このことはとても励みになる。
私が以前言ったように、プログラミングの実験は相対的に小さなプログラムで実行されてきた。 私的には、そのプログラミングの実験が本当に大きなプログラムのより信頼性の高い構成を実現する方法を見せてくれると固く信じている。 けれども、私は未だに「本当に大きなプログラム」についての実験的証拠を持っていないことを強調したい。 今までに得た実験的証拠は、私が試した大きさのプログラムに限れば、プログラムを構成する能力が増大することを示している。 私はそれを試みたのであるが、誰かが大人数の雇用を望むときに必要とされるようなプログラム開発の必要条件をほとんど認識していないと感じている。 私は中国陸軍式の手法 [6-5] の経験がないし、その長所についても確信していない。