構造化プログラミング

ウィキペディアから、無料の百科事典
ナビゲーションにジャンプ 検索にジャンプ

構造化プログラミングは、選択( if / then / else)および繰り返し(whileおよびfor)、ブロック構造の構造化制御フロー構造を広範囲に使用することにより、コンピュータープログラムの明快さ、品質、および開発時間を改善することを目的としたプログラミングパラダイムです。 、およびサブルーチン

1950年代後半に、ALGOL58およびALGOL60プログラミング言語[1]が登場し、後者にはブロック構造のサポートが含まれるようになりました。その人気と広く受け入れられている要因には、最初は学界で、後には実務家の間で、1966年に構造化プログラム定理として知られているものの発見[2]や、影響力のある「有害と見なされるステートメントへの移動」の公開が含まれます「構造化プログラミング」という用語を生み出したオランダのコンピューター科学者EdsgerW.Dijkstraによる1968年の公開書簡。[3]

構造化プログラミングは、例外処理を実行する必要がある場合など、特定の場合に、より明確なプログラムを可能にする偏差で最も頻繁に使用されます。

要素

制御構造

構造化プログラム定理に従うと、すべてのプログラムは制御構造で構成されていると見なされます。

  • "順序"; 順序付けられたステートメントまたはサブルーチンが順番に実行されます。
  • "選択"; プログラムの状態に応じて、1つまたは複数のステートメントが実行されます。これは通常、などのキーワードif..then..else..endifで表されます。条件ステートメントには少なくとも1つの真の条件が必要であり、各条件には最大で1つの出口点が必要です。
  • "反復"; ステートメントまたはブロックは、プログラムが特定の状態に達するか、コレクションのすべての要素に操作が適用されるまで実行されます。これは通常、、、、などのキーワードでwhile表さrepeatforますdo..until多くの場合、各ループには1つのエントリポイントのみを含めることをお勧めします(元の構造プログラミングでは、1つの出口ポイントのみを使用し、いくつかの言語でこれを強制します)。
  • "再帰"; ステートメントは、終了条件が満たされるまで自分自身を繰り返し呼び出すことによって実行されます。実際には反復ループと似ていますが、再帰ループは計算効率が高く、カスケードスタックとして実装方法が異なります。
NS図(青)とフローチャート(緑)を使用した、シーケンス、選択、繰り返しの3つの基本パターンのグラフィック表現

サブルーチン

サブルーチン; プロシージャ、関数、メソッド、サブプログラムなどの呼び出し可能なユニットは、シーケンスを単一のステートメントで参照できるようにするために使用されます。

ブロック

ブロックは、ステートメントのグループを1つのステートメントであるかのように扱うことができるようにするために使用されます。ブロック構造化言語には、 ALGOL 68if..fiのように括弧で囲まれたifステートメントPL / IPascalのように括弧で囲まれたコードセクション、 Pythonのよう空白のインデントなど、何らかの形式的な方法で構造を囲むための構文があります。Cおよびそれ以降の多くの言語中括弧BEGIN..END{...}

構造化プログラミング言語

手続き型プログラミング言語のようなものを使用することが望ましいですが、任意のプログラミング言語で構造化プログラミングを行うことができます[引用が必要] [説明が必要]構造化プログラミングに最初に使用された言語には、ALGOLPascalPL / IAdaなどがありますが、それ以降のほとんどの新しい手続き型プログラミング言語には、構造化プログラミングを促進する機能が含まれており、意図的に残されていることもあります。非構造化プログラミングをより困難 にするために、機能(特にGOTO)を提供します。構造化プログラミング(モジュラープログラミング[要出典]とも呼ばれます)は、作成中のプログラムに論理構造を適用して、プログラムをより効率的かつ理解しやすく、変更しやすくします。

歴史

理論的基礎

構造化プログラム定理は、構造化プログラミングの理論的基礎を提供します。計算可能な関数を表現するには、プログラムを組み合わせる3つの方法(シーケンス、選択、反復)で十分であると述べていますこの観察は、構造化プログラミングの動きに端を発するものではありませんでした。これらの構造は中央処理装置の命令サイクルとチューリングマシンの動作を説明するのに十分です。したがって、プロセッサは、メモリから読み取る命令が構造化プログラムの一部でなくても、この意味で常に「構造化プログラム」を実行しています。ただし、著者は通常、BöhmとJacopiniによる1966年の論文の結果を信用しています。ダイクストラはこの論文を自分で引用しました。[4]構造化プログラムの定理は、有用な構造化プログラムを作成および分析する方法を扱っていません。これらの問題は、1960年代後半から1970年代初頭にかけて対処され、DijkstraRobert W. FloydTony HoareOle-Johan DahlDavidGriesが多大な貢献をしました。

ディベート

構造化プログラミングの早期採用者であるPJPlaugerは、構造化プログラムの定理に対する彼の反応について次のように述べています。

私たちの改宗者は、この興味深いニュースを、再構築されていないアセンブリ言語プログラマーの鼻の下で振り回しました。BöhmとJacopiniによる証明も、構造化コードの記述での繰り返しの成功も、彼らが納得する準備ができたよりも1日早く彼らをもたらしませんでした。[5]

ドナルド・クヌースは、プログラムは証明可能性を念頭に置いて作成する必要があるという原則を受け入れましたが、GOTOステートメントを廃止することに同意しませんでした(そして、それでも[要出典]に同意しません)。1974年の論文「Gotoステートメントを使用した構造化プログラミング」[6]で、彼は、直接ジャンプが証明可能性を犠牲にすることなく、より明確でより効率的なコードにつながると信じている例を示しました。クヌースは、より緩い構造上の制約を提案しました。左側にすべての前方分岐、右側にすべての後方分岐があり、互いに交差する分岐がないプログラムのフローチャートを描くことができるはずです。コンパイラグラフ理論に精通している人の多く還元可能なフローグラフのみを許可することを提唱しています[として定義されている場合?][誰?]

構造化プログラミング理論家は、 IBMの研究者Harlan Millsが構造化プログラミング理論の解釈をニューヨークタイムズの研究ファイルの索引付けシステムの開発に適用した後、1970年代に主要な同盟国を獲得しました。このプロジェクトはエンジニアリングで大きな成功を収め、ダイクストラはミルズの解釈が公開された作品と異なる点を批判しましたが、他の企業のマネージャーは構造化プログラミングの採用を支持してプロジェクトを引用しました。[要出典]

1987年になってからも、コンピュータサイエンスジャーナルで構造化プログラミングの問題を提起することは可能でした。フランク・ルービンはその年に「 『GOTOは有害であると考えられる』」というタイトルの公開書簡でそうしました。[7]ルービンと他の作家が彼に返答したときに行った譲歩の両方を鋭く批判したダイクストラからの返答を含む、多くの反対意見が続いた。

結果

20世紀の終わりまでに、ほぼすべてのコンピューター科学者は、構造化プログラミングの概念を学び、適用することが有用であると確信していました。FORTRANCOBOLBASICなど、元々プログラミング構造がなかった高級プログラミング言語に、現在はそれらがあります。

一般的な逸脱

gotoは現在、選択(if / then / else)と繰り返し(whileとfor)の構造化構造に大部分が置き換えられていますが、純粋に構造化されている言語はほとんどありません。多くの言語で見られる最も一般的な逸脱は、サブルーチンを早期に終了するためのreturnステートメントの使用です。これにより、構造化プログラミングで必要とされる単一の出口点ではなく、複数の出口点が得られます。純粋に構造化されたプログラミングでは厄介なケースを処理するための他の構造があります。

早期終了

構造化プログラミングからの最も一般的な逸脱は、関数またはループの早期終了です。関数のレベルでは、これはreturnステートメントです。ループのレベルでは、これはbreakステートメント(ループを終了する)またはcontinueステートメント(現在の反復を終了し、次の反復に進む)です。構造化プログラミングでは、ブランチやテストを追加することでこれらを複製できますが、ネストされたコードからの戻り値の場合、これによりかなり複雑になる可能性があります。Cは、これらの構成の初期の顕著な例です。一部の新しい言語には「ラベル付きブレーク」もあり、これにより、最も内側のループ以上のものからブレークアウトできます。例外も早期終了を許可しますが、さらに結果が生じるため、以下で扱います。

複数の出口は、さまざまな理由で発生する可能性があります。ほとんどの場合、サブルーチンに実行する作業がない(値を返す場合は計算が完了している)か、継続を妨げる「例外的な」状況に遭遇したため、必要になります。例外処理。

早期終了で最も一般的な問題は、クリーンアップまたは最終ステートメントが実行されないことです。たとえば、割り当てられたメモリの割り当てが解除されなかったり、開いているファイルが閉じられなかったりして、メモリリークリソースリークが発生します。これらは各返品サイトで実行する必要があります。これは脆弱であり、バグが発生しやすいためです。たとえば、後の開発では、returnステートメントが開発者によって見落とされる可能性があり、サブルーチンの最後に実行する必要のあるアクション(たとえば、traceステートメント)がすべての場合に実行されるとは限りません。標準のPascalSeed7など、returnステートメントのない言語では、この問題は発生しません。

最新の言語のほとんどは、そのようなリークを防ぐために言語レベルのサポートを提供します。[8]リソース管理の詳細な説明を参照してください最も一般的には、これはアンワインド保護を介して実行されます。これにより、実行がブロックを終了するときに特定のコードが実行されることが保証されます。これは、クリーンアップブロックとを使用する代わりの構造化された方法gotoです。これは、ほとんどの場合、例外処理try...finally,として知られ、その一部と見なされます。例外なく導入する複数のステートメントの場合、奇妙に見えるかもしれません。リソース管理をカプセル化するためのさまざまな手法が存在します。主にC ++で見られる代替アプローチは、リソース取得は初期化ですreturntry...finally,、関数出口で通常のスタックアンワインド(変数の割り当て解除)を使用して、ローカル変数のデストラクタを呼び出してリソースの割り当てを解除します。

Kent BeckMartin Fowler、および共著者は、リファクタリングの本で、ネストされた条件は、ガード句によって予測される複数の出口を使用する特定のタイプのよりフラットな構造よりも理解しにくい可能性があると主張しています。彼らの2009年の本は、「1つの出口点は実際には有用な規則ではありません。明確さが重要な原則です。方法が1つの出口点でより明確な場合は、1つの出口点を使用します。そうでない場合は使用しないでください」と明確に述べています。これらは、ネストされた条件のみで構成される関数を、保護されたreturn(またはthrow)ステートメントのシーケンスに変換するためのクックブックソリューションを提供し、その後に、一般的なケースのコードを含むことを目的とした単一の保護されていないブロックが続きます。あまり一般的ではないもの(またはエラー)を処理することになっています。[9] HerbSutterAndreiAlexandrescuは、2004年のC ++のヒント集でも、単一出口ポイントは廃止された要件であると主張しています。[10]

彼の2004年の教科書で、David Wattは、「シングルエントリーマルチエグジット制御フローがしばしば望ましい」と書いています。Tennentのシーケンサーのフレームワーク概念を使用する、Wattは、現代のプログラミング言語に見られる制御フロー構造を一律に説明し、複数出口制御フローのコンテキストで特定のタイプのシーケンサーが他のタイプよりも好ましい理由を説明しようとします。Wattは、ジャンプのターゲットである実際のラベルまたはアドレスをリーダーが見つけて調べるまで、ジャンプの宛先はプログラムのリーダーにとって自明ではないため、無制限のgoto(ジャンプシーケンサー)は悪いと書いています。対照的に、Wattは、リターンシーケンサーの概念的な意図は、その宛先を調べる必要なしに、それ自体のコンテキストから明らかであると主張しています。ワットは、エスケープシーケンサーとして知られているシーケンサーのクラスを書いています、「テキストで囲むコマンドまたはプロシージャの実行を終了するシーケンサー」として定義され、ループからのブレーク(マルチレベルブレークを含む)とreturnステートメントの両方を含みます。ワットはまた、ジャンプシーケンサー(ゴト)はCのような言語ではいくらか制限されており、ターゲットはローカルブロックの内側または外側のブロックを包含している必要がありますが、その制限だけではCのゴトの意図を自己にするのに十分ではないことにも注意してください。 -説明しているので、「スパゲッティコード」を生成できます。Wattは、例外シーケンサーがエスケープおよびジャンプシーケンサーとどのように異なるかについても調べます。これについては、この記事の次のセクションで説明します。[11]

上記とは対照的に、バートランド・メイヤーは2009年の教科書に、「羊の服を着たばかりの老人」のような指示を書き、breakそのcontinue使用gotoを強く勧めています。[12]

例外処理

Ariane 501の災害によるコーディングエラーに基づいて、ソフトウェア開発者のJim Bonangは、関数からスローされた例外は単一出口パラダイムに違反していると主張し、すべてのプロシージャ間の例外を禁止する必要があると提案しています。Bonangは、すべての単一出口準拠のC ++を次のように記述することを提案しています。

bool MyCheck1 ()throw (){   
  bool success = false ;   
  {を試してください 
    //例外をスローする可能性のあることを実行します。
if MyCheck2 ()){      
      SomeInternalException ();をスローします。 
    }
    //上記と同様の他のコード。
成功= true ;      
  } catch (...){   
    //キャッチされ、ログに記録されたすべての例外。
}  
  成功を返す; 
}

Peter Ritchieはまた、原則として、関数内のthrow直前の1つでもreturn単一出口の原則に違反することを指摘しますが、Dijkstraのルールは、例外処理がプログラミング言語のパラダイムになる前の時期に作成されたと主張しています。単一のリターンポイントに加えて、任意の数のスローポイントを許可することを提案します。彼は、単一出口を作成するために例外をラップするソリューションは、ネストの深さが深く、したがって理解するのがより難しいと指摘し、カーゴカルト思考に従事する例外をサポートするプログラミング言語にそのようなソリューションを適用することを提案する人々を非難します[13]

David Wattは、シーケンサーのフレームワークでの例外処理も分析します(この記事の前のセクションで早期終了について紹介しました)。Wattは、異常な状況(通常、算術オーバーフローやファイルが見つからないなどの入出力エラーなど)は一種であると述べています。 「一部の低レベルのプログラムユニットで検出されますが、ハンドラーはより自然に高レベルのプログラムユニットに配置されます」というエラーのエラー。たとえば、プログラムにファイルを読み取るための複数の呼び出しが含まれている場合でも、ファイルが見つからない場合に実行するアクションは、プログラムに対する問題のファイルの意味(目的)に依存するため、この異常な状況の処理ルーチンはできません。低レベルのシステムコードにあります。Wattsはさらに、発信者にステータスフラグテストを導入すると、シングルエグジット構造化プログラミングまたは(マルチエグジット)リターンシーケンサーが伴うように、「アプリケーションコードはステータスフラグのテストによって乱雑になる傾向があり」、「プログラマーは忘れてまたは怠惰にテストを省略している可能性があります。ステータスフラグ。実際、ステータスフラグで表される異常な状況は、デフォルトでは無視されます。」彼は、ステータスフラグのテストとは対照的に、例外には反対のことがあると述べています。デフォルトの動作。プログラマーが何らかの方法で例外を明示的に処理しない限り、プログラムを終了させます。おそらく、意図的に無視するコードを追加することによって。これらの議論に基づいて、Wattは、ジャンプシーケンサーまたはエスケープシーケンサー(前のセクションで説明)は、上記のセマンティクスを備えた専用の例外シーケンサーほど適切ではないと結論付けています。[14]

whileLoudenとLambertの教科書は、制御の転送が実際の転送が行われる場所とは異なるプログラムのポイントで設定されるため、例外処理がループのような構造化プログラミング構造とは異なることを強調しています。転送が実際に行われるポイントで、制御が実際に移管されるという構文上の兆候がない可能性があります。」[15]コンピュータサイエンスのArvindKumar Bansal教授は、例外処理を実装する言語ではfor、例外がない場合に単一出口プロパティを持つのような制御構造でさえ、例外が時期尚早に発生する可能性があるため、例外が存在する場合はもはやそれを持たないと述べています。制御構造の任意の部分で早期終了を引き起こします。たとえばinit()、で例外をスローした場合for (init(); check(); increm())、その後、check()後の通常の出口点に到達しません。[16]他の人による複数の先行研究(1999-2004)と彼ら自身の結果を引用して、WestleyWeimerとGeorgeNeculaは、例外を伴う重大な問題は、「プログラマーが推論するのが難しい隠れた制御フローパスを作成する」ことであると書いています。 。[17]

コードを単一出口ポイントに制限する必要性は、OpenMPなどの並列コンピューティングに焦点を当てたいくつかの現代的なプログラミング環境で見られます。のようなOpenMPのさまざまな並列構造parallel doでは、並列構造の内側から外側への早期終了は許可されていません。この制限には、C ++例外からのすべての種類の出口が含まれますがbreak、ジャンプターゲットも並列構造内にある場合、これらすべてが並列構造内で許可されます。[18]

複数のエントリ

ごくまれに、サブプログラムで複数のエントリが許可されます。これは、最も一般的には、コルーチン(またはジェネレーター/セミコルーチン)への入力のみであり、サブプログラムが制御(および場合によっては値)を生成しますが、中断したところから再開できます。特にストリームの場合、このようなプログラミングには多くの一般的な用途があります。(特に入力/出力)、ステートマシン、および同時実行性。コード実行の観点からは、コルーチンからの譲歩は、サブルーチンから戻るよりも構造化プログラミングに近いです。サブプログラムは実際には終了しておらず、再度呼び出されたときに続行されるためです。これは早期終了ではありません。ただし、コルーチンとは、サブルーチンの単一の呼び出しスタックではなく、複数のサブプログラムが実行状態を持っていることを意味します。したがって、異なる形式の複雑さが導入されます。

この場合、プログラムの状態(変数値など)が初期化されていないかあいまいであるため、サブプログラムがサブプログラム内の任意の位置に入ることができることは非常にまれであり、これはgotoと非常によく似ています。

ステートマシン

一部のプログラム、特にパーサー通信プロトコルには、基本構造に簡単に縮小できない方法で相互に続くいくつかの状態があり、一部のプログラマーは、新しい状態にジャンプして状態変更を実装しますこのタイプの状態切り替えは、Linuxカーネルでよく使用されます。[要出典]

ただし、各状態を個別のサブプログラムに変更し、変数を使用してアクティブな状態を示すことにより、これらのシステムを構築することができます(トランポリンを参照)。あるいは、これらはトランポリンを不要にするコルーチンを介して実装できます。

も参照してください

参考文献

引用

  1. ^ クラーク、レスリーB.ウィルソン、ロバートG。; ロバート、クラーク(2000)。比較プログラミング言語(第3版)。ハーロウ、イギリス:アディソン-ウェスリー。p。20. ISBN 97802017101202015年11月26日にオリジナルからアーカイブされました2015年11月25日取得
  2. ^ Böhm&Jacopini1966
  3. ^ Dijkstra 1968、p。147、「go toステートメントを無制限に使用すると、プロセスの進行状況を説明するための意味のある座標セットを見つけることが非常に困難になります。...現状のgotoステートメントはあまりにも原始的です。 、自分のプログラムを台無しにするのはあまりにも多くの招待です。」
  4. ^ ダイクストラ1968
  5. ^ Plauger、PJ(1993年2月12日)。目的のプログラミング、ソフトウェア設計のエッセイ(第1版)。プレンティスホール。p。 25ISBN 978-0-13-721374-0
  6. ^ ドナルドE.クヌース(1974年12月)。「gotoステートメントを使用した構造化プログラミング」(PDF)コンピューティング調査6(4):261–301。土井10.1145 /356635.356640S2CID207630080_ 2013-10-23にオリジナル(PDF)からアーカイブされました。  
  7. ^ フランク・ルービン(1987年3月)。「」「GOTOConsideredHarmful」「ConsideredHarmful」 (PDF)。ACMコミュニケーション。30(3):195–196。doi10.1145/214748.315722。S2CID6853038。2009-03-20 オリジナルPDF)からアーカイブ
  8. ^ 長老、マット; ジャクソン、スティーブ; Liblit、Ben(2008年10月)。コードサンドイッチ(PDF)(テクニカルレポート)。ウィスコンシン大学マディソン校1647年。
  9. ^ ジェイフィールズ; シェーンハービー; マーティンファウラー; ケントベック(2009)。リファクタリング:RubyEditionピアソン教育。pp。274–279。ISBN 978-0-321-60350-0
  10. ^ ハーブサッター; アンドレイアレキサンドレスク(2004)。C ++コーディング標準:101のルール、ガイドライン、およびベストプラクティスピアソン教育。ISBN 978-0-13-265442-5例4:単一のエントリ、単一の出口( "SESE")。歴史的に、一部のコーディング標準では、各関数に1つの出口、つまり1つのreturnステートメントが必要でした。このような要件は、例外とデストラクタをサポートする言語では廃止されています。この場合、関数には通常、多数の暗黙的な出口があります。
  11. ^ Watt&Findlay 2004、pp。215–221。
  12. ^ バートランドメイヤー(2009)。タッチオブクラス:オブジェクトとコントラクトをうまくプログラムする方法を学ぶシュプリンガーサイエンス&ビジネスメディア。p。189. ISBN 978-3-540-92144-8
  13. ^ 「シングルエントリー、シングルエグジット、それでもオブジェクト指向言語に適用できるべきか?」PeterRitchieのMVPブログ2012年11月14日にオリジナルからアーカイブされました2014年7月15日取得
  14. ^ Watt&Findlay 2004、pp。221–222。
  15. ^ ケネスC.ラウデン; ケネスA.ランバート(2011)。プログラミング言語:原則と実践(第3版)。センゲージラーニング。p。423. ISBN 978-1-111-52941-3
  16. ^ Arvind Kumar Bansal(2013)。プログラミング言語入門CRCプレス。p。135. ISBN 978-1-4665-6514-2
  17. ^ Weimer、W。&Necula、GC(2008)。「例外的な状況とプログラムの信頼性」(PDF)プログラミング言語とシステムに関するACMトランザクション30(2)。8:27。土井10.1145 /1330017.1330019S2CID3136431_ 2015年9月23日にオリジナル(PDF)からアーカイブされました  
  18. ^ Rohit Chandra(2001)。OpenMPでの並列プログラミングモーガンカウフマン。p。45. ISBN 978-1-55860-671-5

ソース

外部リンク

  • BPStruct-並行システム(プログラム、プロセスモデル)を構築するためのツール
  • J.ダーリントン; M.ガネム; HW To(1993)、「構造化並列プログラミング」、超並列コンピューターのプログラミングモデル。IEEE Computer SocietyPress。1993:160–169、CiteSeerX  10.1.1.37.4610