コンピュータプログラミングにおいて、ソースコードのブロックとは、上から下へ順番に実行されるステートメントの集まりのことです。ブロック構造は構造化プログラミングの基本であり、制御構造はブロックを用いて構築されます。構文的には、ブロックは単一のステートメントとよく似た働きをします。なぜなら、制御構造は通常、単一のステートメントまたはブロックのいずれかに対して動作するからです。
ブロックの性質はプログラミング言語によって異なりますが、典型的な特徴があります。ブロックは通常、可視性スコープを提供し、ブロック内で定義された識別子は、外側の包含ブロックからは見えませんが、同じ名前の識別子によってマスクされていない限り、内側のネストされたブロックからは見えます。ブロックは通常、ライフタイムスコープを提供し、識別子に関連付けられたリソースは、制御フローが識別子が定義されたブロックの終わりに達したときに、部分的または完全に解放されます。
ブロックの概念は、最初のオートコードの開発中に 1950 年代に開発され、Algol 58およびAlgol 60レポートで形式化されました。Algol 58 では、制御フローのみに関連する「複合ステートメント」の概念が導入されました。[ 1 ]その後、Algol 60 の構文と意味を説明した改訂レポートでは、ブロックとブロック スコープの概念が導入され、ブロックは「一連の宣言とそれに続く一連のステートメントで構成され、begin と end で囲まれています...」で構成され、「すべての宣言はブロック内でこのように出現し、そのブロックに対してのみ有効です。」[ 2 ]
ブロックはコード内で独立した単位として区切られますが、構文は言語によって異なります。代表的な例としては、以下のようなものがあります。
(、 とを使用します)。if ~ then ~ elif ~ else ~ ficase ~ in ~ out ~ esacfor ~ while ~ do ~ odブロック内での宣言をサポートする言語の中には、すべての宣言をサポートしていないものもあります。例えば、多くのC言語系の言語では、ブロック内での関数定義は許可されていません。また、Pascalは、その祖先であるAlgolとは異なり、既存のブロックのbeginとend内で独自の宣言を持つブロックの使用をサポートしておらず、複合ステートメントのみをサポートし、ステートメントのシーケンスをif、、、whileおよびrepeatその他の制御ステートメントでグループ化できるようにしています。
コンピュータ黎明期には、FORTRAN IVやBASICなどの多くの言語には、基本的なループ構造を除いてブロック構文がありませんでした。条件分岐はgoto文を使って記述されていました。以下のFORTRAN 66コードでは、ブロック構文がないため、論理構造が分かりにくくなっています。
C 計算する値を初期化しますPAYSTX = . FALSE . PAYSST = . FALSE . TAX = 0.0 SUPTAX = 0.0 C 従業員の収入が課税しきい値より少ない場合は、税金控除をスキップしますIF ( WAGES . LE . TAXTHR ) GOTO 100 PAYSTX = . TRUE . TAX = ( WAGES - TAXTHR ) * BASCRT C 従業員の収入がスーパー税のしきい値より少ない場合は、スーパー税控除をスキップしますIF ( WAGES . LE . SUPTHR ) GOTO 100 PAYSST = . TRUE . SUPTAX = (賃金- SUPTHR ) * SUPRAT 100課税対象額=賃金-税金- SUPTAXブロックを使用すると、プログラマはステートメントのグループを単位として扱うことができ、このスタイルのプログラミングでは初期化時に必要だったデフォルト値を、ブロック構造を使用することで、判断の近くに配置できます。Jensen and Wirth Pascal の次のコードは、ブロック構造によって、コードをどのようにリファクタリングして明確化できるかが分かりやすくなり、また、内側の条件の構造を外側の条件から完全に移動させることができ、その影響も容易に予測できるため、リファクタリングも容易になることを示しています。以下のコード断片でブロックを使用すると、プログラマの意図が明確になり、結果として得られるブロックを組み合わせて、条件ステートメントのネストされた階層構造を作成できます。コードの構造はプログラマの思考をより忠実に反映しているため、理解しやすく、変更も容易になります。内側の if ステートメントを外側の if ステートメントから完全に分離し、2 つのブロックを順番に配置して連続して実行することで、コードをさらに明確にすることができます。意味論的には、この場合ほとんど違いはなく、読みやすさを高めるためのインデントによってサポートされたブロック構造を使用することで、プログラマーはコードを容易にリファクタリングできます。
if wages > tax_threshold then begin paystax := true ; tax := ( wages - tax_threshold ) * tax_rate if wages > supertax_threshold then begin pays_supertax := true ; supertax := ( wages - supertax_threshold ) * supertax_rate end else begin pays_supertax := false ; supertax := 0 end end else begin paystax := false ; pays_supertax := false ; tax := 0 ; supertax := 0 end ; taxed := wages - tax - supertax ;原始的な言語では、変数のスコープは広範でした。例えば、Fortranのサブルーチンの一部で、IEMPNOという整数変数が従業員の社会保障番号(SSN)を表すために使われていたとします。しかし、同じサブルーチンの保守作業中に、プログラマーが誤って同じ変数IEMPNOを別の目的で使用してしまうと、追跡が困難なバグが発生する可能性があります。ブロック構造を用いることで、プログラマーはスコープをより容易に制御できるようになります。
以下の R5RS 標準スキームの断片では、empno はマネージャーと部下をそれぞれ SSN で識別するために使用されていますが、部下の SSN は内側のブロック内で宣言されているため、マネージャーの SSN を含む同名の変数とは相互作用しません。実際には、明確さを考慮するとプログラマは異なる変数名を選択する可能性が高いですが、選択の余地があり、意図せずバグを混入させる可能性は低くなります。ラムダ式内では、変数 empno は部下の SSN を参照します。外側の式でマネージャーの SSN を参照する変数 empno はシャドウイングされています。
( let (( empno ( ssn-of employee-name ))) ( while ( is-manager empno ) ( let (( employees ( length ( underlings-of empno )))) ( printf "~a には ~a 人の従業員がいます:~%" employee-name employees ) ( for-each ( lambda ( empno ) ( printf "名前: ~a、役割: ~a~%" ( name-of empno ) ( role-of empno ))) ( underlings-of empno )))))一部の言語では、囲まれたブロック内でも関数スコープで変数を宣言できます。たとえば、JavaScript では、で宣言された変数はvar関数スコープを持ちます。