カレーや部分塗布の何が特別ですか?


9

私は関数型プログラミングに関する記事を毎日読んでおり、可能な限りいくつかのプラクティスを適用しようとしています。しかし、私はカレーや部分的なアプリケーションで何がユニークなのか理解していません。

例として、このGroovyコードを見てみましょう。

def mul = { a, b -> a * b }
def tripler1 = mul.curry(3)
def tripler2 = { mul(3, it) }

私は違いが何であるかを理解していないtripler1tripler2。どちらも同じではないですか?「カレー化」は、Groovy、Scala、Haskellなどの純粋または部分的な関数型言語でサポートされています。ただし、別の名前付きまたは匿名を作成するだけで、同じこと(左カレー、右カレー、nカレー、または部分アプリケーション)を実行できます。tripler2ほとんどの言語(Cでも)でパラメーターを元の関数(など)に転送する関数またはクロージャー

ここで何か不足していますか?Grailsアプリケーションでカレーや部分的なアプリケーションを使用できる場所はありますが、「どうして違うの?」

教えてください。

編集:皆さんは、デフォルトのパラメーターを元の関数に転送する別の関数を作成/呼び出すよりも、部分的なアプリケーション/カリー化が単に効率的であると言っていますか?


1
誰かが「カレー」または「カレー」のタグを作成できますか?
Vigneshwaran

どのようにCでカレーしますか?
Giorgio

これはおそらく、実際には部分的なアプリケーションプログラマー
。stackexchange.com/ questions / 152868 /…

1
@Vigneshwaran:カレーをサポートする言語で別の関数を作成する必要はありません。たとえば、Haskellでは、これは1つのintパラメータを取る関数であることをf x y = x + y意味しfます。f x(にf適用x)の結果は、1つのintパラメータを取る関数です。結果f x y(または(f x) yf x適用されるy)は、入力パラメーターをとらず、reduceによって評価される式x + yです。
Giorgio

1
同じことを達成できますが、Cで行う労力ははるかに苦痛であり、デフォルトの動作であるhaskellのような言語ほど効率的ではありません
Daniel

回答:


8

カリー化とは、n個の入力を受け取る関数を、それぞれ1個の入力を受け取るn個の関数に変換/表現することです。部分的な適用とは、関数への入力の一部を修正することです。

部分的に適用する動機は、主に、高次の関数ライブラリを簡単に作成できるようにすることです。たとえば、C ++ STLのアルゴリズムはすべて主に述語または単項関数を受け取ります。bind1stを使用すると、ライブラリユーザーは値がバインドされた非単項関数をフックできます。したがって、ライブラリライターは、単項関数を使用してバイナリバージョンを提供するすべてのアルゴリズムに対して、オーバーロードされた関数を提供する必要はありません。

カレー自体は便利です。自由に好きな場所bind1stに部分的に適用できるからです。つまり、部分的に適用するような機能はもう必要ありません。


curryingグルーヴィーなものや言語を越えて適用できるものは何ですか?
両生類、

その言語間で適用される何かが、皮肉にもグルーヴィーなカレーが本当にそれは行いません@foampile programmers.stackexchange.com/questions/152868/...
JKを。

@jk。カリー化/部分的なアプリケーションは、別の関数を作成して呼び出すよりも効率的だと言っていますか?
Vigneshwaran

2
@Vigneshwaran-必ずしもパフォーマンスが高いとは限りませんが、プログラマーの時間の点で明らかに効率的です。また、カレーは多くの関数型言語でサポートされていますが、通常、オブジェクト指向言語や手続き型言語ではサポートされていません。(または、少なくとも、言語自体によってではありません。)
スティーブンC

6

しかし、ほとんどの言語でパラメーターを元の関数(tripr2など)に転送する別の名前付き関数または匿名関数またはクロージャーを作成するだけで、同じこと(左カリー、右カリー、nカリー、または部分的なアプリケーション)を実行できます( Cさえ)

そしてオプティマイザはそれを見て、すぐにそれが理解できるものに進みます。カリー化はエンドユーザーにとってはちょっとしたトリックですが、言語設計の観点からははるかに優れています。それはです本当に単項など、すべてのメソッドを処理するために素敵なA -> B場所をB別の方法であってもよいです。

これにより、高次関数を処理するために記述する必要のあるメソッドが簡素化されます。言語での静的分析と最適化には、既知の方法で動作する1つのパスしかありません。パラメータバインディングは、この一般的な動作を実行するためにフープを必要とするのではなく、設計から外れます。


6

@jkとして。ほのめかしたように、カリー化はコードをより一般的にするのに役立ちます。

たとえば、次の3つの関数(Haskell)があるとします。

> let q a b = (2 + a) * b

> let r g = g 3

> let f a b = b (a 1)

ここでの関数fは2つの関数を引数として取り1、最初の関数に渡し、最初の呼び出しの結果を2番目の関数に渡します。

fusing qr引数として呼び出すと、効果的に次のようになります。

> r (q 1)

どこqに適用され1て別の関数を返すか(qカリー化)。この返された関数はr、引数として渡される引数として渡されます3。この結果はの値になり9ます。

ここで、他に2つの関数があるとします。

> let s a = 3 * a

> let t a = 4 + a

私たちはこれらを渡すことができf、同様の値を取得する7か、15私たちの引数がなかったかどうかに応じて、s tまたはt s。これらの関数はどちらも関数ではなく値を返すため、f s tまたはで部分的な適用は行われませんf t s

私たちが書いていた場合fqし、r心の中で私たちは、例えば、代わりに部分的なアプリケーションのラムダ(匿名関数)を使用した可能性があります:

> let f' a b = b (\x -> a 1 x)

しかし、これはの一般性を制限していたでしょうf'f引数qand rおよびsandおよびtf'呼び出すことができますが、qandおよびr-でのみ呼び出すことができf' s tf' t s両方ともエラーになります。

もっと

f'が3 つ以上の引数をとるq'/ r'ペアで呼び出された場合でもq'、はq'部分的にに適用されf'ます。

あるいは、内側でqfなく外側をラップすることもできますが、それでは厄介なネストされたラムダが残ります。

f (\x -> (\y -> q x y)) r

基本的にカレーqはそもそもカレーでした!


あなたは私の目を開いた。あなたの答えは、カリー化/部分的に適用された関数が、元の関数に引数を渡す新しい関数を作成することとはどのように異なるかを理解させてくれました。1.カリー化/ペイド関数(f(q.curry(2)など)を渡すことは、一時的な使用だけのために不必要に別の関数を作成するよりも簡単です(groovyなどの関数型言語で)
Vigneshwaran

2.私の質問では、「Cでも同じことができる」と述べました。はい。ただし、関数としてデータを渡すことができない非関数型言語では、パラメーターを元の関数に転送する別の関数を作成しても、カリー化/ paの利点がすべてありません
Vigneshwaran

私はGroovyがHaskellがサポートする一般化のタイプをサポートしていないことに気づきました。私が書かなければならなかったdef f = { a, b -> b a.curry(1) }作るためにf q, r仕事をし、def f = { a, b -> b a(1) }またはdef f = { a, b -> b a.curry(1)() }のためのf s, t仕事に。すべてのパラメーターを渡すか、カレーしていることを明示的に伝える必要があります。:(
Vigneshwaran

2
@Vigneshwaran:はい、Haskellとカレーはとてもうまく調和していると言っても安全です。;] Haskellでは、関数は(正しい定義で)デフォルトでカリー化され、空白は関数の適用を示すためf x y、多くの言語がf(x)(y)でなくを記述することを意味しf(x, y)ます。おそらく、次のqように呼び出されることを期待するように記述した場合、コードはGroovyで機能しq(1)(2)ます
CAマッキャン、

1
@Vigneshwaran嬉しいです!部分的な適用をしていることを明示的に言わなければならないことにあなたの苦痛を感じます。Clojureで私はやらなければなりません-Haskellに(partial f a b ...)慣れているので、他の言語でプログラミングするときに適切なカレーができません(最近、F#で作業していますが、ありがたいことに、それをサポートしています)。
ポール・

3

部分適用には2つのポイントがあります。1つ目は構文/利便性です。@ jkで述べたように、一部の定義は読み書きが簡単で短くなります。(これがいかに素晴らしいかについては、Pointfreeプログラミングをチェックしてください!)

2番目は@telastynが述べたように、関数のモデルに関するものであり、単に便利なだけではありません。Haskellバージョンでは、部分的なアプリケーションで他の言語に慣れていないため、ここからサンプルを取得しますが、すべての関数は単一の引数を取ります。はい、機能は次のとおりです。

(:) :: a -> [a] -> [a]

単一の引数を取ります。関数型コンストラクタの結合性のため->、上記は次と同等です。

(:) :: a -> ([a] -> [a])

これは、aを受け取って関数を返す関数[a] -> [a]です。

これにより、次のような関数を記述できます。

($) :: (a -> b) -> a -> b

これは、適切なタイプの引数に任意の関数を適用できます。次のようなクレイジーなものでも:

f :: (t, t1) -> t -> t1 -> (t2 -> t3 -> (t, t1)) -> t2 -> t3 -> [(t, t1)]
f q r s t u v = q : (r, s) : [t u v]

f' :: () -> Char -> (t2 -> t3 -> ((), Char)) -> t2 -> t3 -> [((), Char)]
f' = f $ ((), 'a')  -- <== works fine

さて、それは人為的な例でした。しかし、より便利なのは、このメソッドを含むApplicative型classです。

(<*>) :: Applicative f => f (a -> b) -> f a -> f b

ご覧のとおり、型はビット$を削除した場合と同じApplicative fで、実際、このクラスはコンテキストでの関数の適用を記述しています。したがって、通常の関数アプリケーションの代わりに:

ghci> map (+3) [1..5]  
[4,5,6,7,8]

Applicativeコンテキストで関数を適用できます。たとえば、何かが存在するか欠落している可能性があるMaybeコンテキストでは、次のようになります。

ghci> Just map <*> Just (+3) <*> Just [1..5]
Just [4,5,6,7,8]

ghci> Just map <*> Nothing <*> Just [1..5]
Nothing

ここで本当にクールな部分は、Applicative型クラスが複数の引数の関数について何も言及していないことです。それでも、次のような6つの引数の関数でも処理できますf

fA' :: Maybe (() -> Char -> (t2 -> t3 -> ((), Char)) -> t2 -> t3 -> [((), Char)])
fA' = Just f <*> Just ((), 'a')

私の知る限り、一般的な形のApplicative型クラスは、部分的な適用の概念がないと不可能です。(そこにいるプログラミングの専門家に-私が間違っている場合は修正してください!)もちろん、言語に部分的なアプリケーションがない場合、何らかの形で構築することができます、それは同じではありません。 ?:)


1
Applicativeカレーや部分的なアプリケーションなしで使用しますfzip :: (f a, f b) -> f (a, b)。高階関数を持つ言語では、これはカリー化と部分的な適用をファンクターのコンテキストに引き上げることを可能にし、と同等(<*>)です。より高次の関数がなければfmap、すべてが役に立たなくなるでしょう。
CAマッキャン

@CAMcCannフィードバックをありがとう!私はこの答えで頭の中にいることを知っていました。だから私が言ったことは間違っていますか?

1
それは確かに精神的に正しいです。「一般的な形」、「可能」、「部分的な適用の概念」の定義で髪を分割しても、f <$> x <*> yカリー化と部分的な適用は簡単に機能するので、魅力的な慣用的なスタイルが簡単に機能するという単純な事実は変わりません。つまり、ここでできることよりも楽しいことのほうが重要です。
CA McCann

関数型プログラミングのコード例を見るたびに、それが手の込んだ冗談であり、存在しないことを確信しています。
Kieveli 2013

1
@Kieveliそれはあなたがそのように感じて残念です。あり、多くの 罰金の チュートリアルあなたを取得し、基本をよく理解して実行しますそこには。
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.