コンピュータプログラミングにおいて、return文は、現在のサブルーチンの実行を中断させ、そのサブルーチンを呼び出した命令の直後のコード上の位置(リターンアドレスと呼ばれる)から実行を再開させます。リターンアドレスは、呼び出し元のルーチンによって保存され、現在では通常、プロセスのコールスタックまたはレジスタに格納されます。多くのプログラミング言語のreturn文では、関数が呼び出し元のコードに返す戻り値を指定できます。
CおよびC++では、(は式) は、関数にプログラムの実行を呼び出し元の関数に戻し、 の値を報告するように指示するステートメントです。関数の戻り値の型がvoidの場合、return ステートメントは値なしで使用できます。この場合、プログラムは現在の関数から抜け出し、呼び出し元の関数に戻ります。[ 1 ] [ 2 ]同様の構文は、 Modula-2 [ 3 ]やPython [ 4 ]などの他の言語でも使用されています。return exp;expexp
Pascalにはreturn文はありません。関数やプロシージャは最後のステートメントに到達すると自動的に戻ります。関数からの戻り値は、関数と同じ名前の識別子に代入することで関数内で提供されます。[ 5 ]ただし、Pascalの一部のバージョンでは、関数からすぐに値を返す、またはパラメータなしでプロシージャからすぐに戻るために使用できる特別な関数が提供されています。 [ 6 ]Exit(exp);
Pascal と同様に、FORTRAN II、Fortran 66、Fortran 77、およびそれ以降のバージョンのFortranでは、関数名への代入によって戻り値を指定しますが、return ステートメントも備えています。このステートメントは戻り値を指定せず、関数に対しては、関数名に代入された値を返します。[ 5 ] [ 7 ] [ 8 ]
他の言語では、関数識別子の代わりにユーザー定義の出力パラメータが使用される場合がある。 [ 9 ]
Oberon ( Oberon-07 ) は、return ステートメントの代わりに return 句を使用します。return 句は、プロシージャ本体の最後のステートメントの後に配置されます。[ 10 ]
Lisp、Perl、Rubyなどの式指向プログラミング言語では、プログラマーが明示的なreturn文を省略し、代わりに最後に評価された式をサブルーチンの戻り値として指定することができます。その他の場合、明示的なreturn文がない場合はNull値が返されます。Pythonでは、 return文が省略された場合に値が返されます[ 4 ]。JavaScriptでは値が返されます。Noneundefined
Windows PowerShellでは、キャプチャされなかった評価済みの式 (たとえば、変数に代入された式、voidにキャストされた式、または$nullにパイプされた式) はすべて、サブルーチンから配列の要素として返されます。キャプチャされなかったオブジェクトが 1 つだけの場合は、単一のオブジェクトとして返されます。
Perlでは、サブルーチンの戻り値は、呼び出し元のコンテキストによって異なります。最も基本的な違いは、呼び出し元のコードが1つの値を期待するスカラーコンテキスト、呼び出し元のコードが値のリストを期待するリストコンテキスト、および呼び出し元のコードが戻り値を全く期待しないvoidwantarrayコンテキストです。サブルーチンは、関数を使用してコンテキストをチェックできます。引数なしの特別なreturn構文は、スカラーコンテキストでは未定義の値を、リストコンテキストでは空のリストを返すために使用されます。スカラーコンテキストは、さらにブール型、数値型、文字列型、およびさまざまな参照型コンテキストに分割できます。また、コンテキスト依存オブジェクトは、スカラー値の遅延評価を伴うコンテキスト戻りシーケンスを使用して返すことができます。
多くのオペレーティングシステムでは、プログラムがプロセス終了時に(通常の出力とは別に)結果を返すことができます。これらの値は終了ステータスと呼ばれます。このようにして渡せる情報量はかなり限られており、実際には成功または失敗を通知する程度に限られることが多いです。プログラム内部からこの戻り値を取得するには、通常、Exit(システムコール)を呼び出す必要があります(C言語でも一般的で、C言語ではmain関数から戻るという別のメカニズムも利用できます)。
return文には様々な形式があります。最も一般的な構文は以下のとおりです。
例えばMOS Technology 6502のような一部のアセンブリ言語では、「RTS」(ReTurn from Subroutine:サブルーチンからの復帰)というニーモニックが使われています。
明示的なreturn文を持つ言語では、同じ関数内に複数のreturn文が存在する可能性がある。それが良いことなのかどうかは議論の余地がある。
構造化プログラミングの熱心な支持者は、各関数が単一のエントリと単一の出口(SESE)を持つようにしています。そのため、サブルーチンのテキストの末尾以外では明示的な return 文の使用を避けるべきだと主張されています[ 14 ] 。なぜなら、return 文を「早期に終了する」ために使用すると、GOTO文で発生するのと同じような問題が発生する可能性があるからです。逆に、return 文を使用することで、より複雑なコード(例えば、より深いネストなど)になり、可読性が損なわれる場合には、return 文を使用する価値があると主張できます。
デイビッド・ワットは、2004年の著書の中で、「単一エントリ・複数出口の制御フローはしばしば望ましい」と述べている。ワットは、テネントのフレームワーク概念であるシーケンサーを用いて、現代のプログラミング言語に見られる制御フロー構造を統一的に記述し、複数出口制御フローの文脈において、特定の種類のシーケンサーが他のシーケンサーよりも好ましい理由を説明しようとしている。ワットは、無制限のgoto(ジャンプシーケンサー)は、ジャンプ先のラベルやアドレスを読者が見つけて調べるまで、ジャンプ先がプログラムの読者にとって自明ではないため、好ましくないと述べている。これに対し、リターンシーケンサーの概念的な意図は、ジャンプ先を調べる必要なく、その文脈から明らかであるとワットは主張する。さらにワットは、 「テキストで囲まれたコマンドまたはプロシージャの実行を終了するシーケンサー」と定義されるエスケープシーケンサーと呼ばれるシーケンサーのクラスには、ループからのbreak (多段階breakを含む)とreturn文の両方が含まれると述べている。ワットはまた、ジャンプシーケンサー(goto)はC言語のように、ターゲットがローカルブロック内またはそれを囲む外側のブロックでなければならないという制約がある一方で、その制約だけではC言語のgotoの意図を自己記述的にするには不十分であり、依然として「スパゲッティコード」を生成する可能性があると指摘している。ワットはまた、例外シーケンサーがエスケープシーケンサーやジャンプシーケンサーとどのように異なるかについても検討している。これの詳細については、構造化プログラミングに関する記事を参照のこと。[ 15 ]
エリック・S・ロバーツが引用した実証研究によると、学生プログラマーは、複数の終了ポイントを許可しないパスカルのような言語で、いくつかの単純な問題に対する正しい解決策を策定するのに苦労した。配列内の要素を線形に検索する関数を作成する問題について、ヘンリー・シャピロによる1980年の研究(ロバーツが引用)では、パスカルが提供する制御構造のみを使用した場合、正しい解決策を提示できたのは被験者のわずか20%であったのに対し、ループの途中から戻り値を記述することを許可した場合、この問題に対して誤ったコードを記述した被験者はいなかったことがわかった。[ 16 ]
ケント・ベックやマーティン・ファウラーなど他の研究者は、関数の冒頭付近にある条件付きの「早期終了」return文であるガード句を1つ以上使用することで、代替案よりも関数が読みやすくなることが多いと主張している。[ 17 ] [ 18 ] [ 19 ] [ 20 ]
早期終了で最もよくある問題は、クリーンアップやfinal文が実行されないことです。例えば、割り当てられたメモリが解放されなかったり、開いているファイルが閉じられなかったりして、メモリリークが発生します。これらは各戻り点で実行する必要がありますが、これは脆弱で、バグが発生しやすいです。例えば、開発の後半で、開発者がreturn文を見落とし、サブルーチンの最後に実行すべきアクション(トレース文など)が必ずしも実行されない可能性があります。標準Pascalのようにreturn文を持たない言語には、この問題はありません。C++やPythonなどの一部の言語では、戻り時(または例外スロー時)にアクションを自動的に実行できる概念を採用しており、これらの問題の一部を軽減しています。これらは「try/finally」などと呼ばれます。このような「finally」句のような機能は、サブルーチンの単一の戻り点へのgoto文によって実装できます。別の解決策としては、関数終了時に通常のスタックアンワインディング(変数解放)を使用してリソースを解放する方法があります。例えば、ローカル変数のデストラクタを使用したり、Pythonの「with」文のような同様のメカニズムを使用したりする方法があります。
初期のPascalやCなどの言語の実装では、コンパイラを簡素化するために、関数が返すことができる型を制限していました(たとえば、レコード型や構造体型をサポートしていませんでした)。
Javaや、 JavaScriptなど Java をモデルにした類似の言語では、 try-catch 構造のfinallyブロックが常に実行されるため、return 文の後でもコードを実行することが可能です。したがって、return文がtryまたはcatchブロック内のどこかに配置されている場合、 finallyブロック内のコード(追加されている場合) が実行されます。exit もその後に発生するため、非プリミティブ型の戻り値 (既に返されたオブジェクトのプロパティ) を変更することも可能です。[ 21 ]
return文の類義語としてyield文があります。return文はサブルーチンを終了させますが、yield文はコルーチンを一時停止させます。コルーチンは、再度呼び出された場合、一時停止した箇所から処理を再開します。コルーチンはサブルーチンよりも実装がかなり複雑なため、yield文はreturn文ほど一般的ではありませんが、多くのプログラミング言語で使用されています。
ハードウェアの命令セットによっては、以下のような様々な呼び出し/戻りシーケンスが可能です。
CALL命令のアドレスをスタックにプッシュし、指定されたアドレスに分岐します。この命令は、スタックから戻りアドレスを命令ポインタにポップし、そのアドレスから実行を再開します。(例:x86、PDP-11 ) Motorola 96000などのアーキテクチャでは、スタック領域は、メインメモリのアドレス空間とは別の、スタックメモリ空間と呼ばれる別のアドレス空間に割り当てられる場合があります。[ 22 ] [ 23 ] NEC μPD7720も、独自の別のアドレス空間を持つスタックを備えています。[ 24 ]RETURNCALL命令のアドレスをレジスタに格納し、指定されたアドレスに分岐します。命令シーケンスは、レジスタから戻りアドレスを命令ポインタに格納し、そのアドレスから実行を再開します。(例:IBM System/360およびz/Architectureまでの後継システム、ほとんどのRISCアーキテクチャ)RETURNCALL)命令のアドレスを呼び出しアドレスの記憶場所に格納し、指定されたアドレス+1に分岐します。命令シーケンスは、サブルーチンの最初の命令への間接ジャンプによって、戻りアドレスに分岐します。(例: IBM 1130、SDS 9XX、PDP-8)RETURN{{cite book}}: CS1 maint: 数値名: 著者リスト (リンク){{cite book}}: CS1 maint: 数値名: 著者リスト (リンク)... 出口が 1 つしかないという考え方... 私はメソッドから出口が 1 つしかないというルールには従いません。