クロージャは関数型プログラミングにおける重要な文法構造です。関数型プログラミングはプログラミング パラダイムです (手続き型プログラミングとオブジェクト指向プログラミングもプログラミング パラダイムです)。プロセス指向プログラミングでは関数を見てきましたが、オブジェクト指向プログラミングではオブジェクトを見てきました。関数とオブジェクトの基本的な目的は、コードを特定の論理的な方法で編成し、コードの再利用性を向上させることです。クロージャはコードを整理するための構造でもあり、コードの再利用性も向上します。
言語が異なれば、クロージャの実装方法も異なります。 Python は関数オブジェクトに基づいており、クロージャの構文構造のサポートを提供します (Python がオブジェクトを使用して、特殊なメソッドやマルチパラダイムで特殊な構文を実装していることを何度も見てきました)。 Python ではすべてがオブジェクトであり、関数の文法構造もオブジェクトです。関数オブジェクトでは、関数オブジェクトの名前を変更したり、関数オブジェクトをパラメータとして渡したりするなど、通常のオブジェクトと同じように関数オブジェクトを使用します。
関数オブジェクトのスコープ
他のオブジェクトと同様に、関数オブジェクトにも独自の生存範囲があり、それが関数オブジェクトのスコープです。関数オブジェクトは def ステートメントを使用して定義され、関数オブジェクトのスコープは def が配置されているレベルと同じです。たとえば、次のコードでは、line_conf 関数の所属スコープ内で定義した関数行は、line_conf の所属スコープ内でのみ呼び出すことができます。
line_conf()
print(line(5)) # 範囲外
同様に、ラムダを使用して関数を定義する場合、関数オブジェクトのスコープはラムダが配置されているレベルと同じになります。
閉店
関数はオブジェクトなので、関数の戻り結果として使用できます。
my_line = line_conf()
print(my_line(5))
上記のコードは正常に実行できます。 line_confの戻り結果はlineオブジェクトに代入されます。上記のコードは 11 を出力します。
line() の定義で外部変数が参照された場合はどうなりますか?
b = 5
my_line = line_conf()
print(my_line(5))
上記のコードは 25 を出力します。つまり、line で参照される b 値は、関数オブジェクトの定義時に参照できる b 値であり、使用時の b 値ではありません。
関数とその環境変数は一緒になってクロージャを形成します。 Python では、いわゆるクロージャは、環境変数の値を含む関数オブジェクトです。環境変数の値は、関数オブジェクトの __closure__ 属性に格納されます。たとえば、次のコード:
b = 5
my_line = line_conf()
print(my_line.__closure__)
print(my_line.__closure__[0].cell_contents)
__closure__ にはタプルが含まれています。このタプルの各要素はセル型のオブジェクトです。最初のセルには整数 15 が含まれていることがわかります。これは、クロージャを作成したときの環境変数 b の値です。
クロージャの実際的な例を見てみましょう:
line1 = line_conf(1, 1)
line2 = line_conf(4, 5)
print(line1(5), line2(5))
閉包がない場合、直線関数を作成するたびに a、b、x を指定する必要があります。この方法では、より多くのパラメーターを渡す必要があり、コードの移植性が低下します。クロージャを使用して、実際に関数を作成します。線関数は広義の関数を定義します。この関数の一部の側面はすでに決定されています (直線である必要があります) が、他の側面 (a および b パラメーターなど) は未決定です。続いて、line_conf によって渡されたパラメータに基づいて、クロージャの形式で最終関数を決定します。
クロージャと並列操作
クロージャは、関数が定義する必要があるパラメータの数を効果的に減らします。これは並列操作に重要な意味を持ちます。並列コンピューティング環境では、各コンピューターに機能を担当させ、あるコンピューターの出力を次のコンピューターの入力と連結できます。最終的には、一連のコンピューター クラスターの一方の端からデータを入力し、もう一方の端からデータを出力する、組み立てラインのように作業します。この状況は、パラメーター入力が 1 つだけの関数に最適です。クロージャはこの目的を達成できます。
並列コンピューティングがホットスポットになりつつあります。これは、関数型プログラミングが再び人気を集めている重要な理由でもあります。関数型プログラミングは 1950 年代には存在していましたが、あまり広く使用されていませんでした。ただし、上で説明したパイプライン化された作業並列クラスタリング プロセスは、関数型プログラミングに完全に適しています。関数型プログラミングの自然な利点により、ますます多くの言語が関数型プログラミング パラダイムのサポートを追加し始めています。