クロージャ

コンピュータサイエンスでは、クロージャーとは、それ自体が環境を持つ関数のことです。この環境には、少なくとも1つのバインド変数(数値などのを持つ名前)があります。クロージャの環境は、クロージャが使用されるまでの間、束縛変数をメモリ内に保持します。

Peter J. Landinは1964年にこのアイデアにクロージャという名前をつけた。クロージャが普及したのは、1975年以降のプログラミング言語「Scheme」です。それ以降に作られた多くのプログラミング言語にもクロージャが搭載されている。

匿名関数(名前のない関数)は、間違ってクロージャと呼ばれることがある。無名関数を持つほとんどの言語にはクロージャもある。無名関数は、少なくとも1つの束縛された変数を持つ自分自身の環境を持っていれば、クロージャでもある。自身の環境を持たない無名関数はクロージャではない。名前付きクロージャは匿名ではない。

クロージャとファーストクラスの関数

には、数字や文字などの他の種類のデータや、より単純なパーツで構成されたデータ構造などがあります。プログラミング言語のルールでは、関数に与えることができる値、関数が返すことができる値、変数名に束縛される値を第一級の値としている。また、他の関数を受け取ったり返したりする関数を高次関数と呼ぶ。関数を一級値とする言語の多くは、高階の関数やクロージャも持っている。

例えば、次のようなScheme関数を見てみましょう。

; 少なくともTHRESHOLD部数が販売された全ての書籍のリストを返します。(define (best-selling-books threshold) (filter (lambda (book) (≧ (book-sales book) threshold)) book-list)

この例では、ラムダ式 (lambda (book) (>= (book-sales book) threshold)) が関数 best-selling-books の一部となっています。関数が実行されると、Schemeはラムダ式の値を作らなければなりません。これはラムダのコードと、ラムダ内の自由変数であるしきい値変数への参照を持つクロージャを作ることで行われます。(自由変数とは、値に束縛されていない名前のことです)。

フィルタ関数は、リストの各ブックに対してクロージャを実行し、返すべきブックを選びます。クロージャ自身がしきい値への参照を持っているので、filterがクロージャを実行するたびに、クロージャはその値を使うことができます。filter関数自体は、全く別のファイルに書くこともできます。

同じ例をECMAScript(JavaScript)で書き直してみました。これはクロージャをサポートするもう一つの人気言語です。

// function bestSellingBooks(threshold) { return bookList. filter( function(book) { return book. sales >= threshold; }.     ); }

ECMAScriptでは、ここでlambda代わりにfunctionという単語を使い、filter関数の代わりにArray.filterメソッドを使っていますが、それ以外は同じことを同じように行っているコードです。

関数は、クロージャを作成してそれを返すことができます。次の例は、関数を返す関数です。

スキームで

; fの導関数を近似する関数を返す ; dxの間隔を使って、適切に小さくする必要がある。 (define (derivative f dx) (lambda (x) (/ (- (f (+ x dx)))(f x)) dx))

ECMAScriptでは

// 適当に小さくしたdxの区間を使って、 // fの微分を近似する関数を返す。 function derivative(f, dx) { return function(x) { return (f(x + dx) - f(x))/ dx; }; }

クロージャ環境では、囲み関数(導関数)が戻った後も、束縛変数fdxが保持されます。クロージャのない言語では、これらの値は囲み関数が戻った後に失われてしまう。クロージャを持つ言語では、どのクロージャでも結合変数を持っている限り、結合変数をメモリに保持しなければならない。

クロージャは、無名関数を使って形成する必要はありません。例えば、Pythonは、無名関数のサポートが限られていますが、クロージャを備えています。例えば、上記のECMAScriptの例をPythonで実装する方法の1つは、次のようになります。

# def derivative(f, dx): def gradient(x): return (f(x + dx) - f(x))/ dx return gradient

この例では、gradientという関数が、変数fdxとともにクロージャを作っています。外側にある derivative という関数は、このクロージャを返します。この場合、無名関数でも問題ありません。

def derivative(f, dx): return lambda x: (f(x + dx) - f(x))/ dx

Pythonでは、ラムダ式が他の(値を返すコード)のみを含み、(効果を持つが値を持たないコード)を含まないため、しばしば名前付き関数を代わりに使用しなければならない。しかし、Schemeのような他の言語では、すべてのコードは値を返しますが、Schemeではすべてが式です。

クロージャの使い方

クロージャーには様々な用途があります。

  • ソフトウェアライブラリの設計者は,重要な関数の引数としてクロージャを渡すことで,ユーザが動作をカスタマイズできるようにすることができます.例えば、値をソートする関数は、ユーザが定義した基準に従ってソートされる値を比較するクロージャ引数を受け入れることができます。
  • クロージャは評価を遅らせる、つまり呼び出されるまで何もしないので、制御構造の定義に使うことができます。例えば、分岐(if/then/else)やループ(whileやfor)など、Smalltalkの標準的な制御構造はすべて、メソッドがクロージャを受け入れるオブジェクトを使って定義されています。ユーザーが独自の制御構造を定義することも簡単です。
  • 同じ環境に閉じた複数の機能を作り、その環境を変えることでプライベートなコミュニケーションを図ることができます(割り当て可能な言語で)。

スキーム内

(define foo #f) (define bar #f) (let ((secret-message "none")) (set! foo (lambda (msg) (set! secret-message msg)) (set! bar (lambda () secret-message))) (display (bar)) ; prints "none" (newline) (foo "meet me by the docks at midnight") (display (bar)) ; prints "meet me by the docks at midnight"

注:字句環境を束ねるデータ構造をクロージャーと呼ぶ人もいますが、通常は関数に限定して呼ばれます。

質問と回答

Q:コンピュータサイエンスにおけるクロージャとは何ですか?


A: クロージャとは、それ自身の環境を持つ関数のことです。

Q: クロージャの環境は何を含んでいますか?


A:クロージャの環境には、少なくとも1つの境界変数が含まれています。

Q:クロージャの名前をつけたのは誰ですか?


A: 1964年、ピーター・J・ランディンがクロージャのアイデアを命名しました。

Q:1975年以降にクロージャを普及させたプログラミング言語は?


A: 1975年以降、Schemeというプログラミング言語がクロージャを普及させました。

Q: 無名関数とクロージャは同じものですか?


A:無名関数はクロージャと間違って呼ばれることがありますが、すべての無名関数がクロージャというわけではありません。

Q: 無名関数がクロージャである理由は何ですか?


A: 無名関数は、少なくとも1つの束縛された変数を持つそれ自身の環境を持っていれば、クロージャです。

Q: 名前付きクロージャは匿名ですか?


A: いいえ、名前付きクロージャは匿名ではありません。

AlegsaOnline.com - 2020 / 2023 - License CC3