11

部分関数適用とクロージャの関係は何かと尋ねられました。私が要点を見逃していない限り、私は何もないと言うでしょう。私がPythonで書いていて、次のように定義された非常に単純な関数 MySum があるとしましょう:

MySum = lambda x, y : x + y;

ここで、1 つのパラメーターを修正して、同じパラメーターで呼び出した場合に MySum が返す値と同じ値を返す小さいアリティの関数を取得します (部分的な適用):

MyPartialSum = lambda x : MySum(x, 0);

Cでまったく同じことができます:

int MySum(int x, int y) { return x + y; }
int MyPartialSum(int x) { return MySum(x, 0); }

だから、ばかげた質問は次のとおりです。違いは何ですか? 部分的なアプリケーションにクロージャが必要なのはなぜですか? これらのコードは同等です。クロージャーと部分適用の境界がわかりません。

4

5 に答える 5

7

部分関数適用とは、特定の関数のいくつかの引数を修正して、引数の少ない別の関数を生成することです。

sum = lambda x, y: x + y
inc = lambda x: sum(x, 1)

「inc」は、コンテキストから何もキャプチャせずに部分的に適用された「合計」であることに注意してください(閉鎖について言及したように)。

しかし、そのような手書きの (通常は匿名の) 関数はちょっと面倒です。内部関数を返す関数ファクトリを使用できます。内部関数は、そのコンテキストからいくつかの変数をキャプチャすることでパラメーター化できます。

# sum = lambda x, y: x + y
def makePartialSumF(n):
    def partialSumF(x):
        return sum(x, n)
    return partialSumF

inc = makePartialSumF(1)
plusTwo = makePartialSumF(2)

ここでは、ファクトリ makePartialSumF が 2 回呼び出されます。各呼び出しは、partialSumF 関数になります (さまざまな値を n としてキャプチャします)。クロージャを使用すると、部分適用の実装が便利になります。したがって、部分適用はクロージャーによって実装できると言えます。もちろん、クロージャーは他の多くのことを行うことができます! (サイド ノードとして、python には適切なクロージャがありません。)

カリー化とは、N 引数の関数を、単項関数を返す単項関数に変換することです。たとえば、3 つの引数を取り、値を返す関数があります。

sum = lambda x, y, z: x + y + z

カレーバージョンは

curriedSum = lambda x: lambda y: lambda z: x + y + z

あなたはそのような python コードを書かないに違いありません。IMO Currying の動機は、に理論的な関心事です。(単項関数のみを使用して計算を表現するフレームワーク:すべての関数は単項です!) 実際的な副産物は、関数がカリー化されている言語では、一部の部分的な適用 (左から引数を「固定」する場合) は、引数を提供するのと同じくらい簡単です。カリー化された関数に。(しかし、すべての部分適用がそのようであるとは限りません。例: 与えられた f(x,y,z) = x+2*y+3*z で、y を定数にバインドして 2 つの変数の関数を生成する場合。)カリー化は、実際に、そして副産物として、多くの有用な部分関数型アプリケーションを些細なものにすることができる手法ですが、それはカリー化のポイントではありません。

于 2012-10-11T19:13:05.320 に答える
5

部分適用は、既存の関数とその引数のサブセットを取得し、残りの引数を受け入れる新しい関数を生成する手法です。

つまり、 function がある場合F(a, b)、 の部分適用を適用する関数はwhereのaようになります。B(fn, a)F(a, b) = B(F, a)(b)

あなたの例では、既存の関数に部分的な適用を適用するのではなく、単に新しい関数を作成しています。

Python での例を次に示します。

def curry_first(fn, arg):
    def foo(*args):
       return fn(arg, *args)
    return foo

これにより、提供された関数と引数に対してクロージャが作成されます。新しい引数シグネチャを持つ最初の関数を呼び出す新しい関数が返されます。クロージャーは重要です - へのfnアクセスを許可しますarg。これで、次のようなことができます。

add = lambda x, y : x + y;
add2 = curry_first(add, 2)
add2(4) # returns 6

私は通常、これをカリー化と呼んでいます。

于 2012-07-21T09:07:45.047 に答える
0

私にとって、partialSum をそのように使用すると、数値を合計するために 1 つの関数 (MySum) のみに依存するようになり、問題が発生した場合のデバッグがはるかに簡単になります。コードの 2 つの異なる部分にコードを挿入します。

将来、MySum のロジックを変更することにした場合 (たとえば、x+y+1 を返すようにするなど)、MySum を呼び出すため、MyPartialSum について心配する必要はありません。

ばかげているように見えても、そのようにコードを記述することは、関数の依存関係のプロセスを単純化するためです。後々の勉強で気付くと思います。

于 2012-07-21T09:05:20.787 に答える