サブルーチン

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

コンピュータプログラミングではサブルーチンは特定のタスクを実行する一連のプログラム命令であり、1つの単位としてパッケージ化されています。このユニットは、特定のタスクを実行する必要があるプログラムで使用できます

サブルーチンは、プログラム内で定義することも、多くのプログラムで使用できるライブラリーで個別に定義することもできます。さまざまなプログラミング言語では、サブルーチンは、ルーチンサブプログラム関数メソッド、またはプロシージャと呼ばれる場合があります。技術的には、これらの用語はすべて異なる定義を持ち、命名法は言語ごとに異なります。一般的な総称の呼び出し 可能ユニットが使用されることがあります。[1]

サブプログラムという名前は、サブルーチンが、より大きなプログラムまたは別のサブプログラムの1つのステップとして使用されるコンピュータープログラムとほぼ同じように動作することを示しています。多くの場合、サブルーチンは、プログラムの1回の実行中に複数回開始し、他のサブルーチンからも含めて複数の場所から開始し、サブルーチンのタスクが完了すると、呼び出し後に次の命令に分岐(戻る)できるようにコーディングされています。 。サブルーチンのアイデアは、ジョン・モークリーがENIACの作業中に最初に考案し[2]、1947年1月のハーバード大学のシンポジウム「EDVACタイプのマシンの問題の準備」に記録されました。[3]モーリス・ウィルクス DavidWheelerStanleyGillは、一般に、この概念の正式な発明であると考えられています。これは、開いたサブルーチンまたはマクロとは対照的に、閉じたサブルーチンと呼ばれます[4] [5][6]しかし、Turingは、1945年の論文で、NPL ACEの設計提案に関するサブルーチンについて議論し、差出人住所スタックの概念を発明しました。[7]

サブルーチンは強力なプログラミングツールであり[8]多くのプログラミング言語の構文には、それらの記述と使用のサポートが含まれています。サブルーチンを賢明に使用すると(たとえば、構造化プログラミングのアプローチを通じて)、大規模なプログラムの開発と保守のコストを大幅に削減すると同時に、その品質と信頼性を高めることができます。[9]サブルーチンは、多くの場合ライブラリに収集され、ソフトウェアを共有および取引するための重要なメカニズムです。オブジェクト指向プログラミングの分野は、オブジェクトメソッドに基づいています(これらのオブジェクトまたはオブジェクトクラスにアタッチされたサブルーチンです)。

スレッデッドコードと呼ばれるコンパイルメソッドでは、実行可能プログラムは基本的に一連のサブルーチン呼び出しです。

主なコンセプト

サブルーチンの内容はその本体であり、サブルーチンが呼び出されたときまたは呼び出されたときに実行されるプログラムコードの一部です。

サブルーチンは、呼び出し元のプログラムから1つ以上のデータ値を取得することを期待するように作成できます(パラメーターまたは仮パラメーターを置き換えるため)。呼び出し側プログラムは、引数と呼ばれるこれらのパラメーターの実際の値を提供します。プログラミング言語が異なれば、引数を渡すために異なる規則を使用する場合があります。

大会 説明 一般的な使用
値で呼び出す 引数が評価され、値のコピーがサブルーチンに渡されます Pascal、Delphi、Simula、CPL、PL / M、Modula、Oberon、Adaなど、Algol60以降のほとんどのAlgolに似た言語のデフォルト。C、C ++、Java(オブジェクトおよび配列への参照も値によって渡されます)
参照による呼び出し 引数への参照、通常はそのアドレスが渡されます Algol 68、Pascal、Delphi、Simula、CPL、PL / M、Modula、Oberon、Adaなど、Algol60以降のほとんどのAlgolに似た言語で選択できます。C ++、Fortran、PL / I
結果による呼び出し パラメータ値は、サブルーチンから戻るときに引数にコピーされます。 AdaOUTパラメーター
値による呼び出し-結果 パラメータ値は、サブルーチンへのエントリ時にコピーされ、戻り時に再びコピーされます。 Algol、Swiftの入出力パラメーター
名前で呼ぶ マクロのように–パラメーターを未評価の引数式に置き換え、呼び出されたルーチンがパラメーターを使用するたびに、呼び出し元のコンテキストで引数を評価します。 Algol、Scala
定数値で呼び出す パラメータが定数として扱われることを除いて、値による呼び出しと同様です PL / I NONASSIGNABLEパラメーター、AdaINパラメーター

サブルーチン呼び出しには、コンピュータメモリ内のデータ構造の変更、周辺機器の読み取りまたは書き込み、ファイルの作成、プログラムまたはマシンの停止、さらにはプログラムの実行の遅延などの副作用もあります。副作用のあるサブプログラムは、同じ引数で呼び出された場合でも、呼び出されるたびに異なる結果を返す場合があります。例として、多くの言語で使用可能な乱数サブルーチンがあります。このサブルーチンは、呼び出されるたびに異なる疑似乱数を返します。副作用のあるサブルーチンが広く使用されているのは、命令型プログラミング言語の特徴です。

サブルーチンは、そのタスクを実行するために、1つ以上の場所で再帰的に呼び出すことができるようにコーディングできます。この方法では、数学的帰納法と再帰的分割統治アルゴリズムによって定義された関数を直接実装できます

1つのブール値関数を計算すること(つまり、yes / noの質問に答えること)を目的とするサブルーチンは、述語と呼ばれることもあります。論理プログラミング言語では、多くの場合[あいまい]すべてのサブルーチンは述語と呼ばれます。これは、それらが主に[あいまい]成功または失敗を決定するためです。[要出典]

値を返さない、またはnull値を返すサブルーチンは、プロシージャと呼ばれることもあります。プロシージャは通常、引数を変更し、手続き型プログラミングの中核部分です


関数

関数は、値を返すサブルーチンです。[10]関数の主な目的は、複雑な計算を意味のあるチャンクに分割し、それらに名前を付けることです。[11]サブルーチンは、計算された値を呼び出し元に返すか(戻り値)、さまざまな結果値または出力パラメーターを提供します。実際、サブルーチンの一般的な使用法は、数学関数を実装することです。この場合、サブルーチンの目的は、サブルーチンに渡された引数によって値が完全に決定される1つ以上の結果を純粋に計算することです。(例には、数値の対数または行列の計算が含まれる場合があります。)一部の言語では、値を返すプロシージャの構文は、RETURNS句などがないことを除いて、値を返さないプロシージャの構文と基本的に同じです。一部の言語では、プロシージャは、引数に応じて、値の有無を動的に選択する場合があります。

言語サポート

高水準プログラミング言語には通常、次のような特定の構造が含まれています。

  • サブルーチンを構成するプログラム(本体)の部分を区切ります
  • サブルーチンに識別子(名前)を割り当てます
  • パラメータと戻り値の名前とデータ型を指定します
  • 一時変数にプライベートネーミングスコープを提供する
  • サブルーチン内でアクセス可能なサブルーチン外の変数を特定します
  • サブルーチンを呼び出す
  • パラメータに値を指定します
  • メインプログラムには、サブプログラムのアドレスが含まれています
  • サブプログラムには、メインプログラムの関数呼び出しの次の命令のアドレスが含まれています
  • 本体内から戻り値を指定します
  • 呼び出し側プログラムに戻る
  • 呼び出しによって返された値を破棄します
  • 通話中に発生した例外的な状態を処理します
  • サブルーチンをモジュールライブラリオブジェクト、またはクラスにパッケージ化します

PascalFortranAda 、およびBASICの多くの方言など、一部のプログラミング言語は、呼び出し元のプログラムに明示的な戻り値を提供する関数または関数サブプログラムと、そうでないサブルーチンまたはプロシージャを区別します。これらの言語では、関数呼び出しは通常、に埋め込まれています(たとえば、関数はとして呼び出される場合があります)。プロシージャ呼び出しは、構文的にステートメントとして動作します(たとえば、プロシージャは、または(など)などのステートメントによって呼び出されるか、明示的に呼び出されます)。sqrty = z + sqrt(x)printif x > 0 then print(x)CALLGOSUBcall print(x)CLispは、関数とサブルーチンを区別しません。

Haskellのような厳密に関数型のプログラミング言語では、サブプログラムに副作用がない可能性があります。つまり、プログラムのさまざまな内部状態は変化しません。同じ引数で繰り返し呼び出された場合、関数は常に同じ結果を返します。値を返さないサブルーチンは、副作用を引き起こす可能性がない限り役に立たないため、このような言語は通常、関数のみをサポートします。

CC ++C#などのプログラミング言語では、サブルーチンは単に関数と呼ばれることもあります(異なる概念である 数学関数関数型プログラミングと混同しないでください)。

言語のコンパイラは通常、プロシージャコールを変換し、明確に定義された呼び出し規約に従ってマシン命令に戻るため、サブルーチンを呼び出すプログラムとは別にサブルーチンをコンパイルできます。callおよびreturnステートメントに対応する命令シーケンスは、プロシージャのプロローグおよびエピローグと呼ばれます。

利点

プログラムをサブルーチンに分割する利点は次のとおりです。

短所

インラインコードを使用する場合と比較して、サブルーチンを呼び出すと、呼び出しメカニズムに計算のオーバーヘッドが発生します。[要出典]

サブルーチンは通常、関数の入口と出口の両方で標準のハウスキーピングコードを必要とします(関数プロローグとエピローグ–通常は汎用レジスタと差出人住所を最小限に 保存します)。

歴史

サブルーチンのアイデアは、コンピューティングマシンがすでにしばらく存在していた後に考案されました。算術および条件付きジャンプ命令は事前に計画されており、変更は比較的少ないですが、プロシージャ呼び出しに使用される特別な命令は、長年にわたって大幅に変更されています。マンチェスターベイビーRCA1802などの初期のコンピューターやマイクロプロセッサーには、単一のサブルーチン呼び出し命令がありませんでした。サブルーチンを実装することもできますが、プログラマーは各呼び出しサイトで呼び出しシーケンス(一連の命令)を使用する必要がありました。

サブルーチンは、1945年に KonradZuseZ4に実装されました。

1945年、Alan M. Turingは、サブルーチンを呼び出したり、サブルーチンから戻ったりする手段として、「埋める」および「埋めない」という用語を使用しました。[12] [13]

1947年1月、ジョン・モークリーは、ハーバード大学と米国海軍兵站局の共同後援の下で、「大規模デジタル計算機械のシンポジウム」で一般的なメモを発表しました。ここで彼は、シリアルおよびパラレル操作について説明します。

...マシンの構造は1ビット複雑である必要はありません。この手順に不可欠なすべての論理特性が利用可能であるため、サブルーチンをメモリ内のマシンが認識している場所に配置するためのコーディング命令を進化させ、それらを簡単に使用できるようにすることができます。

つまり、特定の問題に必要なサブルーチンのリストから、サブルーチンAを除算、サブルーチンBを複素数乗算、サブルーチンCを数列の標準誤差の評価などに指定できます。...これらのサブルーチンはすべてマシンに格納され、コーディングで示されているように、番号で簡単に参照するだけで済みます。[3]

Kay McNultyは、 ENIACチームでJohn Mauchlyと緊密に協力し、第二次世界大戦中にプログラミングしていたENIACコンピューターのサブルーチンのアイデアを開発しました。[14]彼女と他のENIACプログラマーは、ミサイルの軌道を計算するのを助けるためにサブルーチンを使用しました。[14]

GoldstinevonNeumannは、サブルーチンの使用について論じた1948年8月16日付けの論文を書きました。[15]

IBM 1620Intel 4004Intel 8008PICマイクロプロセッサなどの一部の非常に初期のコンピュータとマイクロプロセッサには、専用のハードウェアスタックを使用してリターンアドレスを格納する単一命令のサブルーチン呼び出しがあります。このようなハードウェアは、わずかなレベルしかサポートしていません。サブルーチンのネストですが、再帰的なサブルーチンをサポートできます。UNIVAC IPDP-1IBM 1130など、1960年代半ば以前のマシンは、通常、呼び出し規約を使用します。これにより、呼び出されたサブルーチンの最初のメモリ位置に命令カウンタが保存されました。これにより、任意に深いレベルのサブルーチンのネストが可能になりますが、再帰的なサブルーチンはサポートされません。PDP-11(1970)は、スタックをプッシュするサブルーチン呼び出し命令を備えた最初のコンピューターの1つですこの機能は、任意の深さのサブルーチンのネストと再帰的なサブルーチンの両方をサポートします。[16]

言語サポート

非常に初期のアセンブラでは、サブルーチンのサポートは制限されていました。サブルーチンは、相互に、またはメインプログラムから明示的に分離されておらず、実際、サブルーチンのソースコードは、他のサブプログラムのソースコードと散在している可能性があります。一部のアセンブラは、呼び出しシーケンスと戻りシーケンスを生成するための事前定義されたマクロを提供します。1960年代までに、アセンブラは通常、互いにリンクできるインラインサブルーチンと個別にアセンブルされたサブルーチンの両方をはるかに高度にサポートしていました。

ユーザー作成のサブルーチンと関数をサポートする最初のプログラミング言語の1つは、FORTRANIIでした。IBM FORTRAN IIコンパイラーは1958年にリリースされました。ALGOL58 およびその他の初期のプログラミング言語も、手続き型プログラミングをサポートしていました。

サブルーチンライブラリ

この面倒なアプローチでも、サブルーチンは非常に便利であることがわかりました。一つには、彼らは多くの異なるプログラムで同じコードの使用を許可しました。さらに、初期のコンピュータではメモリが非常に不足していたため、サブルーチンを使用するとプログラムのサイズを大幅に節約できました。

多くの初期のコンピュータは、パンチされた紙テープからプログラム命令をメモリにロードしました次に、各サブルーチンは、メインプログラム(または「メインライン」 [17]の前または後にロードまたはスプライスされた個別のテープによって提供されます。そして、同じサブルーチンテープを多くの異なるプログラムで使用できます。メイン入力にパンチカードを使用するコンピューターに適用される同様のアプローチ。サブルーチンライブラリという名前は、元々、文字通りの意味で、集合的に使用するためにテープまたはカードデッキのインデックス付きコレクションを保持するライブラリを意味していました。

間接ジャンプで戻る

自己変更コードの必要性を排除するために、コンピューター設計者は最終的に間接ジャンプ命令を提供しました。そのオペランドは、リターンアドレス自体ではなく、リターンアドレスを含む変数またはプロセッサーレジスタの場所でした。

これらのコンピューターでは、サブルーチンのリターンジャンプを変更する代わりに、呼び出し元のプログラムがリターンアドレスを変数に格納するため、サブルーチンが完了すると、事前定義された変数で指定された場所に実行を指示する間接ジャンプが実行されます。

サブルーチンにジャンプ

もう1つの進歩は、サブルーチン命令へのジャンプでした。これは、リターンアドレスの保存と呼び出し元のジャンプを組み合わせて、オーバーヘッドを大幅に最小限に抑えました。

たとえば、 IBM System / 360では、プロシージャ呼び出し用に設計された分岐命令BALまたはBALRは、規則レジスタ14によって、命令で指定されたプロセッサレジスタにリターンアドレスを保存します。リターンするには、サブルーチンを実行するだけで済みます。そのレジスタを介した間接分岐命令(BR)。サブルーチンが他の目的(別のサブルーチンの呼び出しなど)でそのレジスタを必要とする場合、レジスタの内容をプライベートメモリの場所またはレジスタスタックに保存します。

HP 2100などのシステムでは、JSB命令は同様のタスクを実行しますが、リターンアドレスがブランチのターゲットであるメモリ位置に格納されている点が異なります。プロシージャの実行は、実際には次のメモリ位置で開始されます。HP 2100アセンブリ言語では、たとえば次のように記述します。

       ..。
       JSB MYSUB(サブルーチンMYSUBを呼び出します。)
 BB ...(MYSUBが完了した後、ここに戻ります。)

メインプログラムからMYSUBというサブルーチンを呼び出します。サブルーチンは次のようにコード化されます

MYSUB NOP(MYSUBの差出人住所のストレージ。)
 AA ...(MYSUBの体の始まり。)
       ..。
       JMP MYSUB、I(呼び出し側プログラムに戻ります。)

JSB命令は、NEXT命令のアドレス(つまりBB)をオペランドとして指定された場所(つまりMYSUB)に配置し、その後NEXTの場所(つまりAA = MYSUB + 1)に分岐しました。次に、サブルーチンは、ロケーションMYSUBに格納されているロケーションに分岐した間接ジャンプJMP MYSUB Iを実行することにより、メインプログラムに戻ることができます。

Fortranおよび他の言語のコンパイラーは、利用可能な場合、これらの命令を簡単に利用できます。このアプローチは、複数レベルの呼び出しをサポートしていました。ただし、サブルーチンの戻りアドレス、パラメーター、および戻り値には固定メモリー位置が割り当てられているため、再帰呼び出しは許可されませんでした。

ちなみに、同様の方法が1980年代初頭にLotus 1-2-3によって使用され、スプレッドシート内の再計算の依存関係を発見しました。つまり、各セルにリターンアドレスを格納するための場所が予約されていました。自然な再計算順序では循環参照が許可されていないため、これにより、メモリ内のスタック用のスペースを予約せずにツリーウォークが可能になります。これは、IBMPCなどの小型コンピュータでは非常に制限されていまし

コールスタック

サブルーチン呼び出しの最新の実装のほとんどは、スタックデータ構造の特殊なケースである呼び出しスタックを使用して、サブルーチン呼び出しと戻りを実装します。プロシージャを呼び出すたびに、スタックの最上位にスタックフレームと呼ばれる新しいエントリが作成されます。プロシージャが戻ると、そのスタックフレームがスタックから削除され、そのスペースが他のプロシージャ呼び出しに使用される場合があります。各スタックフレームには、対応する呼び出しのプライベートデータが含まれます。これには通常、プロシージャのパラメータと内部変数、およびリターンアドレスが含まれます。

呼び出しシーケンスは、通常の命令のシーケンス(縮小命令セットコンピューティング(RISC)および非常に長い命令ワード(VLIW)アーキテクチャでまだ使用されているアプローチ)によって実装できますが、1960年代後半以降に設計された多くの従来のマシンには、その目的。

コールスタックは通常、メモリの連続した領域として実装されます。スタックの最下部がこの領域内の最低アドレスか最高アドレスかは任意の設計上の選択であり、スタックがメモリ内で前方または後方に大きくなる可能性があります。ただし、多くのアーキテクチャは後者を選択しました。[要出典]

一部の設計、特に一部のForth実装では、2つの別々のスタックを使用しました。1つは主に制御情報(リターンアドレスやループカウンターなど)用で、もう1つはデータ用です。前者はコールスタックであるか、そのように機能し、プログラマーは他の言語構造を介して間接的にのみアクセスできましたが、後者はより直接的にアクセスできました。

スタックベースのプロシージャ呼び出しが最初に導入されたとき、重要な動機は貴重なメモリを節約することでした。[要出典]このスキームでは、コンパイラは各プロシージャのプライベートデータ(パラメータ、リターンアドレス、ローカル変数)用にメモリ内に個別のスペースを予約する必要はありません。いつでも、スタックには、現在アクティブな(つまり、呼び出されたがまだ返されていない)呼び出しのプライベートデータのみが含まれます。プログラムが通常ライブラリからアセンブルされる方法のために、数千のサブルーチンを含むプログラムを見つけることは珍しくありませんでした(そして今でもそうです)。[要出典]このようなプログラムの場合、コールスタックメカニズムはかなりの量のメモリを節約できます。実際、コールスタックメカニズムは、自動メモリ管理の最も初期の最も単純な方法と見なすことができます

ただし、コールスタックメソッドのもう1つの利点は、同じプロシージャへのネストされた各呼び出しがプライベートデータの個別のインスタンスを取得するため、 再帰的なサブルーチン呼び出しが可能になることです。

遅延スタッキング

コールスタックメカニズムの欠点の1つは、プロシージャコールとそれに対応するリターンのコストが増加することです。[必要な説明]追加のコストには、スタックポインタのインクリメントとデクリメント(および一部のアーキテクチャでは、スタックオーバーフローのチェック)、および絶対アドレスではなくフレーム相対アドレスによるローカル変数とパラメータへのアクセスが含まれます。コストは、実行時間の増加、プロセッサの複雑さの増加、またはその両方で実現される可能性があります。

このオーバーヘッドは、リーフプロシージャまたはリーフ関数で最も明白であり、好ましくありません。これらは、プロシージャを呼び出さずに戻ります。[18] [19] [20]そのオーバーヘッドを減らすために、多くの最近のコンパイラーは、本当に必要になるまで呼び出しスタックの使用を遅らせようとします。[要出典]たとえば、プロシージャPの呼び出しでは、呼び出されたプロシージャのリターンアドレスとパラメータを特定のプロセッサレジスタに格納し、単純なジャンプでプロシージャの本体に制御を移すことができます。プロシージャPが他の呼び出しを行わずに戻った場合、呼び出しスタックはまったく使用されません。Pの場合別のプロシージャQを呼び出す必要がある場合は、コールスタックを使用して、 Qが戻った後に必要になるレジスタの内容(リターンアドレスなど)を保存します。

CおよびC ++の例

CおよびC ++プログラミング言語では、サブプログラムは関数と呼ばれます(クラス関連付けられている場合はメンバー関数として、そうでない場合はフリー関数[21]としてさらに分類されます)。これらの言語は、関数が値を返さないことを示すために特別なキーワードを使用します。C / C ++関数には、アドレスがパラメーターとして渡される変数の変更などの副作用が発生する可能性があることに注意してください。例: void

void Function1 (){ / *いくつかのコード* / }    

この関数は値を返さないため、スタンドアロン関数として呼び出す必要があります。Function1();

int Function2 (){  
  5を返す; 
}

この関数は結果(数値5)を返し、呼び出しは式の一部にすることができます。x + Function2()

char Function3 int number {   
  char selection [] = { 'S' 'M' 'T' 'W' 'T' 'F' 'S' };         
  選択を返す[番号]; 
}

この関数は、0から6までの数値を、対応する曜日の最初の文字、つまり0から「S」、1から「M」、...、6から「S」に変換します。それを呼び出した結果は、変数に割り当てられる可能性がありますnum_day = Function3(number);

void Function4 int * pointer_to_var {   
  * pointer_to_var ++ ;
}

この関数は値を返しませんが、アドレスがパラメーターとして渡される変数を変更します。で呼び出されFunction4(&variable_to_increment);ます。

SmallBasicの例

()                               'サブルーチンを呼び出します

サブ                              'サブルーチン
    TextWindowを開始します。WriteLine "これはMicrosoftSmall Basicのサブルーチンの例です。"   'サブルーチンの機能
EndSub                                   'サブルーチンを終了します

上記の例でExample()は、サブルーチンを呼び出します。[22]実際のサブルーチンを定義するには、Subキーワードを使用する必要があり、サブルーチン名は。の後に続きますSubコンテンツが続いた後、 EndSub入力する必要があります。

Visual Basic6の例

Visual Basic 6言語では、サブプログラムは関数またはサブまたはクラスに関連付けられている場合はメソッド)と呼ばれます。Visual Basic 6は、と呼ばれるさまざまな用語を使用して、パラメーターとして渡されるものを定義します。デフォルトでは、未指定の変数はバリアント型として登録され、 ByRef(デフォルト)またはByValとして渡すことができますまた、関数またはサブが宣言されると、パブリック、プライベート、またはフレンドの指定が与えられ、宣言されたモジュールまたはプロジェクトの外部からアクセスできるかどうかが決まります。

  • 値による[ByVal] –アドレスを渡す代わりに、値のコピーを渡すことにより、引数の値をプロシージャに渡す方法。その結果、変数の実際の値は、渡されたプロシージャによって変更できません。
  • 参照による[ByRef] –値のコピーを渡す代わりに、変数のアドレスを渡すことにより、引数の値をプロシージャに渡す方法。これにより、プロシージャは実際の変数にアクセスできます。その結果、変数の実際の値は、渡されるプロシージャによって変更される可能性があります。特に指定がない限り、引数は参照によって渡されます。
  • パブリック(オプション)–関数プロシージャがすべてのモジュールの他のすべてのプロシージャにアクセスできることを示します。Option Privateを含むモジュールで使用する場合、この手順はプロジェクト外では使用できません。
  • プライベート(オプション)–関数プロシージャは、宣言されているモジュール内の他のプロシージャにのみアクセスできることを示します。
  • フレンド(オプション)–クラスモジュールでのみ使用されます。Functionプロシージャはプロジェクト全体で表示されますが、オブジェクトのインスタンスのコントローラーには表示されないことを示します。
Private  Function  Function1 ()
    'ここにいくつかのコード
EndFunction 

この関数は値を返さないため、スタンドアロン関数として呼び出す必要があります。Function1

Private  Function  Function2 () as  Integer 
    Function2  =  5 
End  Function

この関数は結果(数値5)を返し、呼び出しは式の一部にすることができます。x + Function2()

Private  Function  Function3 ByVal  intValue  as  Integer  as  String 
    Dim  strArray 6  as  String 
    strArray  =  Array "M"  "T"  "W"  "T"  "F"  "S"  "S" 
    Function3  =  strArray intValue 
End  Function

この関数は、0から6までの数値を、対応する曜日の最初の文字、つまり0から「M」、1から「T」、...、6から「S」に変換します。それを呼び出した結果は、変数に割り当てられる可能性がありますnum_day = Function3(number)

Private  Function  Function4 ByRef  intValue  as  Integer 
    intValue  =  intValue  +  1 
End  Function

この関数は値を返しませんが、アドレスがパラメーターとして渡される変数を変更します。「」で呼び出されFunction4(variable_to_increment)ます。

PL / Iの例

PL / Iでは呼び出されたプロシージャに、文字列の長さや配列の境界など、引数に関する情報を提供する記述子を渡すことができます。これにより、手順をより一般的にすることができ、プログラマーがそのような情報を渡す必要がなくなります。デフォルトでは、PL / Iは参照によって引数を渡します。2次元配列の各要素の符号を変更するための(簡単な)サブルーチンは、次のようになります。

  change_sign:procedure(array);
    array(*、*)floatを宣言します。
    配列=-配列;
    change_signを終了します。

これは、次のようにさまざまな配列で呼び出すことができます。

  / *最初の配列の境界は-5から+10および3から9 * /
  array1(-5:10、3:9)floatを宣言します。
  / * 2番目の配列の境界は1から16および1から16 * /
  array2(16,16)floatを宣言します。
  change_sign(array1);を呼び出します。
  change_sign(array2);を呼び出します。

Pythonの例

Python では、キーワードdefは関数を定義するために使用されます。関数の本体を形成するステートメントは、同じ行で継続するか、次の行で開始してインデントする必要があります。[23]次のサンプルプログラムは、 「Helloworld!」を出力します。次の行に「ウィキペディア」が続きます。

def  simple_function ():
    print 'Hello world!' 
    print 'Wikipedia' 
simple_function ()

ローカル変数、再帰、および再入可能性

サブプログラムは、一定量のスクラッチスペースを利用すると便利な場合があります。つまり、中間結果を保持するためにそのサブプログラムの実行中に使用されるメモリ。このスクラッチスペースに格納されている変数はローカル変数と呼ばれ、スクラッチスペースはアクティベーションレコードと呼ばれます。アクティベーションレコードには通常、サブプログラムの終了時に制御をどこに戻すかを指示 するリターンアドレスがあります。

サブプログラムには、コールサイトの数と性質を指定できます。再帰がサポートされている場合、サブプログラムはそれ自体を呼び出すことさえあり、同じサブプログラムの別のネストされた実行が発生している間、その実行を一時停止させます。再帰は、いくつかの複雑なアルゴリズムを単純化し、複雑な問題を分解するための便利な手段です。再帰言語は通常、呼び出しごとにローカル変数の新しいコピーを提供します。プログラマーがローカル変数の値を呼び出し間で同じに保ちたい場合は、一部の言語で静的と宣言するか、グローバル値または共通領域を使用できます。フィボナッチを見つけるためのC / C ++の再帰サブルーチンの例を次に示します。

int Fib int n {   
  if n <= 1 {    
    nを返す; 
  }
  Fib n - 1 + Fib n - 2 );を返します。       
}

Fortranのような初期の言語は、変数が静的に割り当てられ、差出人住所の場所も割り当てられていたため、最初は再帰をサポートしていませんでした。PDP-8などの1960年代後半以前のほとんどのコンピュータは、ハードウェアスタックレジスタをサポートしていませんでした。[要出典]

PL / ICなどのALGOL以降の最新の言語は、ほとんどの場合スタックを使用します。通常、ほとんどの最新のコンピューター命令セットでサポートされ、サブプログラムの実行ごとに新しいアクティブ化レコードを提供します。このように、ネストされた実行は、進行中の他の中断された実行への影響を気にすることなく、ローカル変数を自由に変更できます。ネストされた呼び出しが蓄積されると、中断されたサブプログラムごとに1つのアクティブ化レコードで構成される呼び出しスタック構造が形成されます。実際、このスタック構造は事実上どこにでもあるため、アクティベーションレコードは一般にスタックフレームと呼ばれます

Pascal、PL / I、Adaなどの一部の言語は、ネストされたサブルーチンもサポートしています。これらのサブルーチンは、外部(親)サブルーチンのスコープ内でのみ呼び出すことができるサブルーチンです。内部サブルーチンは、それらを呼び出した外部サブルーチンのローカル変数にアクセスできます。これは、ディスプレイとも呼ばれるアクティベーションレコード内に追加のコンテキスト情報を格納することによって実現されます

同じサブプログラムの別の実行がすでに進行中であっても、サブプログラムを適切に実行できる場合、そのサブプログラムは再入可能であると言われます。再帰サブプログラムは再入可能である必要があります。再入可能なサブプログラムは、複数のスレッドが互いに干渉することを恐れずに同じサブプログラムを呼び出すことができるため、マルチスレッドの状況でも役立ちます。IBM CICS トランザクション処理システムでは準再入可能性はわずかに制限が少なくなりましたが、多くのスレッドで共有されるアプリケーション・プログラムに対する同様の要件でした。

マルチスレッド環境では、通常、複数のスタックがありますコルーチンまたは遅延評価を完全にサポートする環境では、スタック以外のデータ構造を使用してアクティブ化レコードを格納できます。

オーバーロード

強く型付けされた言語では、同じ名前で、異なるタイプのデータまたは異なるパラメータープロファイルで動作する多数の関数が望ましい場合があります。たとえば、平方根関数は、実数、複素数値、または行列を操作するように定義できます。それぞれの場合に使用されるアルゴリズムは異なり、返される結果も異なる場合があります。同じ名前で3つの別々の関数を作成することにより、プログラマーはデータのタイプごとに異なる名前を覚える必要がないという便利さを持っています。さらに、実数に対してサブタイプを定義して、正の実数と負の実数を分離できる場合、2つの関数を実数に対して記述できます。1つはパラメーターが正の場合に実数を返し、もう1つはパラメーターが正の場合に複素数値を返します。ネガティブ。

オブジェクト指向プログラミングでは、同じ名前の一連の関数が異なるパラメータプロファイルまたは異なるタイプのパラメータを受け入れることができる場合、各関数はオーバーロードされていると言われます。

C ++でのサブルーチンのオーバーロードの例を次に示します。

#include <iostream> 

double Area double h double w { return h * w ; }          

double Area double r { return r * r * 3.14 ; }          

int main (){  
  doublerectangle_area = Area 3、4 ; _ _    
  double circle_area = Area 5 );   

  std :: cout << "長方形の面積は" << rectangle_area << std :: endl ;      
  std :: cout << "円の面積は" << circle_area << std :: endl ;      
}

このコードには、同じ名前の2つの関数がありますが、パラメーターが異なります。

別の例として、サブルーチンは、方向を受け入れるオブジェクトを作成し、画面上のこれらのポイントへのパスをトレースする場合があります。コンストラクターに渡すことができるパラメーターは多数あります(トレースの色、開始xおよびy座標、トレース速度)。プログラマーがコンストラクターが色パラメーターのみを受け入れることができるようにしたい場合は、色のみを受け入れる別のコンストラクターを呼び出すことができます。これにより、他のすべてのパラメーターのデフォルト値のセットを渡すすべてのパラメーターを使用してコンストラクターが呼び出されます( XとYは通常、画面の中央に配置されるか、原点に配置され、速度はコーダーが選択した別の値に設定されます)。

PL / Iには、GENERICさまざまなタイプの引数で呼び出される一連のエントリー参照の総称名を定義する属性があります。例:

  DECLARE gen_name GENERIC(
                      名前WHEN(FIXED BINARY)、
                      炎WHEN(FLOAT)、
                      パス名それ以外の場合
                           );

エントリごとに複数の引数定義を指定できます。「gen_name」を呼び出すと、引数がFIXED BINARYの場合は「name」、FLOATの場合は「flame」などが呼び出されます。引数が一致しない場合は、「pathname」が呼び出されます。

クロージャ

クロージャは、それが作成された環境からキャプチャされたいくつかの変数の値を含むサブプログラムですクロージャは、 JohnMcCarthyによって導入されたLispプログラミング言語の注目すべき機能でした実装によっては、クロージャーが副作用のメカニズムとして機能する場合があります。

規約

サブルーチンのコーディングのための多くの規則が開発されました。多くの開発者は、名前付けに関して、サブルーチンの名前は、特定のタスクを実行するときは動詞、照会を行うときは形容詞、変数の置換に使用するときは 名詞にするというアプローチを採用しています。

一部のプログラマーは、サブルーチンが1つのタスクのみを実行する必要があり、サブルーチンが複数のタスクを実行する場合は、より多くのサブルーチンに分割する必要があると提案しています。彼らは、サブルーチンはコード保守の重要なコンポーネントであり、プログラムでのそれらの役割は明確なままでなければならないと主張しています。

モジュラープログラミング(コードのモジュラー化)の支持者は、各サブルーチンが他のコードへの依存を最小限に抑えるべきであると主張しています。たとえば、グローバル変数の使用はサブルーチンとこれらのグローバル変数の間に緊密な結合を追加するため、この観点の支持者によって一般的に賢明ではないと見なされます。そのような結合が必要ない場合、彼らのアドバイスは、代わりに渡されたパラメーターを受け入れるようにサブルーチンをリファクタリングすることです。ただし、サブルーチンに渡されるパラメーターの数を増やすと、コードの可読性に影響を与える可能性があります。

リターンコード

その主な効果または通常の効果に加えて、サブルーチンは、実行中に発生した可能性のある例外的な状態について呼び出し側プログラムに通知する必要がある場合があります。一部の言語およびプログラミング標準では、これは多くの場合、通常および例外的な条件をエンコードする、サブルーチンによって標準の場所に配置される整数値で ある戻りコードを介して行われます。

サブルーチンからの戻りコードが期待されるIBMSystem / 360では、戻り値は4の倍数になるように設計されることが多く、そのため、多くの場合、直後にある分岐テーブルへの直接の分岐テーブルインデックスとして使用できます余分な条件付きテストを回避するために命令を呼び出し、効率をさらに向上させます。System / 360 アセンブリ言語では、次のように記述します。

           BAL 14、SUBRTN01はサブルーチンに移動し、R14にリターンアドレスを格納します
           B TABLE(15)は、reg 15の戻り値を使用して、ブランチテーブルにインデックスを付けます。
*適切なブランチ命令に分岐します。
表BOK戻りコード= 00GOOD}
           BBADリターンコード= 04無効な入力}ブランチテーブル
           BERROR戻りコード= 08予期しない状態}

サブルーチン呼び出しの最適化

サブルーチンの呼び出しには、引数の受け渡し、サブプログラムへの分岐、呼び出し元への分岐など、実行時のオーバーヘッドが大きくなります。オーバーヘッドには、特定のプロセッサレジスタの保存と復元、呼び出しフレームストレージの割り当てと再利用などが含まれることがよくあります。[必要な例]一部の言語では、各サブルーチン呼び出しは、サブルーチンの戻りコードの自動テストまたは発生する可能性のある例外の処理も意味します。 。オブジェクト指向言語のオーバーヘッドの重要な原因は、メソッド呼び出し に集中的に使用される動的ディスパッチです。

プロシージャに副作用がある場合は適用できない、一見明らかなプロシージャ呼び出しの最適化がいくつかあります。たとえば、式では、2つの呼び出しが異なる結果を返す可能性があるため、(f(x)-1)/(f(x)+1)関数を2回呼び出す必要があります。fさらに、x最初の呼び出しで値が変更された可能性があるため、2番目の呼び出しの前にの値を再度フェッチする必要があります。サブプログラムに副作用があるかどうかを判断することは非常に困難です(実際、ライスの定理により決定不可能です)。したがって、これらの最適化は純粋に関数型プログラミング言語では安全ですが、通常、典型的な命令型プログラミングのコンパイラーは最悪の事態を想定する必要があります。

インライン化

このオーバーヘッドを排除するために使用される方法は、各呼び出しサイトでのサブプログラムの本体のインライン展開またはインライン化です(サブルーチンに分岐して戻るのではありません)。これにより、呼び出しのオーバーヘッドが回避されるだけでなく、コンパイラーは、呼び出し時のコンテキストと引数を考慮に入れることで、プロシージャーの本体をより効果的に最適化できます。挿入された本体は、コンパイラーによって最適化できます。ただし、プログラムにサブルーチンへの呼び出しが1つしかない場合を除いて、インライン化すると通常、コードサイズが大きくなります。

人気メディアの比喩

「サブルーチン」という用語は、1990年代以降、テレビや映画で数え切れないほど使用されてきました。現在に設定されることもあれば、遠い将来に設定されることもありますが、コンピュータープログラミングハッキングを含むプロット要素は、この概念を呼び起こす可能性があります。多くの場合、それはしばしば誤用されます。

も参照してください

参考文献

  1. ^ 米国選挙支援委員会(2007年)。「特別な意味を持つ単語の定義」自主投票システムのガイドライン2012年12月8日にオリジナルからアーカイブされました2013年1月14日取得
  2. ^ Subrata Dasgupta(2014年1月7日)。それはバベッジから始まりました:コンピュータサイエンスの起源オックスフォード大学出版局。pp。155–。ISBN 978-0-19-930943-6
  3. ^ a b J.W. Mauchly、「EDVACタイプのマシンの問題の準備」(1947)、Brian Randell(Ed。)、The Origins of Digital Computers、Springer、1982。
  4. ^ ウィーラー、DJ(1952)。「プログラムでのサブルーチンの使用」(PDF)-ACM '52に関する1952年のACM全国会議(ピッツバーグ)の議事録p。235. doi10.1145 /609784.609816
  5. ^ ウィルクス、MV; ウィーラー、DJ; ギル、S。(1951年)。電子デジタルコンピュータ用のプログラムの準備アディソン-ウェスリー。
  6. ^ Dainith、ジョン(2004)。「」「サブルーチンを開きます。」コンピューティングの辞書」。Encyclopedia.com2013年1月14日取得
  7. ^ Turing、Alan M.(1945)、自動コンピューティングエンジン(ACE)の開発に関する提案に関するAM Turing博士による報告:1946年2月にNPLの実行委員会に提出されました。コープランド、BJ、編で転載。(2005)。アランチューリングの自動コンピューティングエンジンオックスフォード:オックスフォード大学出版局。p。383. ISBN 0-19-856593-3
  8. ^ ドナルドE.クヌース(1997)。The Art of Computer Programming、Volume I:FundamentalAlgorithmsアディソン-ウェスリー。ISBN 0-201-89683-4
  9. ^ O.-J. ダール; EWダイクストラ; CAR Hoare(1972)。構造化プログラミングアカデミックプレス。ISBN 0-12-200550-3
  10. ^ Wilson、Leslie B.(2001)。比較プログラミング言語、第3版アディソン-ウェスリー。p。140. ISBN 0-201-71012-9
  11. ^ Stroustrup、Bjarne(2013)。C ++プログラミング言語、第4版アディソン-ウェスリー。p。307. ISBN 978-0-321-56384-2
  12. ^ チューリング、アラン・マシソン(1946-03-19)[1945]、自動計算エンジン(ACE)の数学部門での開発の提案(注:1946-03-19に国立物理研究所(イギリス)の実行委員会の前に提示されました。)
  13. ^ 大工、ブライアン・エドワード; ドラン、ロバート・ウィリアム(1977-01-01)[1975年10月]。「他のチューリングマシン」コンピュータジャーナル20(3):269–279。土井10.1093 / comjnl /20.3.269(11ページ)
  14. ^ a b ウォルター・アイザクソン(2014年9月18日)。「ENIACの女性に関するウォルター・アイザクソン」フォーチュン2018年12月12日にオリジナルからアーカイブされました2018年12月14日取得
  15. ^ 電子計算装置の問題の計画とコーディング、Pt 2、Vol。3 https://library.ias.edu/files/pdfs/ecp/planningcodingof0103inst.pdf(関連ページについては、pdfの163ページを参照してください)
  16. ^ ガイルイススティールジュニアAIメモ443。 '「高価な手続きの呼び出し」の神話を暴く。または、有害と見なされるプロシージャコールの実装」。セクション「C. プロシージャコールの評判が悪い理由」。
  17. ^ フランク、トーマスS.(1983)。PDP-11とそのアセンブリ言語の紹介Prentice-Hallソフトウェアシリーズ。プレンティスホール。p。195. ISBN  97801349170472016年7月6日取得組み立て担当者に、有用なすべてのサブルーチンのソースコードのコピーを提供し、組み立て用のメインラインプログラムを提示するときに、メインラインで呼び出されるサブルーチンを教えてください[...]
  18. ^ 「ARMインフォメーションセンター」Infocenter.arm.com 2013年9月29日取得
  19. ^ 「x64スタック使用量」MicrosoftDocsMicrosoft 2019年8月5日取得
  20. ^ 「関数タイプ」Msdn.microsoft.com 2013年9月29日取得
  21. ^ 「自由な機能が意味するもの」
  22. ^ 「MicrosoftSmallBasic」www.smallbasic.com
  23. ^ 「4。その他の制御フローツール— Python3.9.7ドキュメント」