29

関数をカリー化する方法に関するチュートリアルは山ほどあり、stackoverflow には多くの質問があります。しかし、The Little Schemer、いくつかの本、チュートリアル、ブログ投稿、stackoverflow スレッドを読んだ後でも、「カリー化のポイントは何ですか?」という単純な質問に対する答えがわかりません。「なぜ?」ではなく、関数をカレー化する方法を理解しています。その後ろに。

カリー化された関数の実用的な使用法を誰かが私に説明してくれませんか (関数ごとに 1 つの引数のみを許可する言語の外では、カリー化を使用する必要性はもちろん明らかです)。

編集: TLSのいくつかの例を考慮に入れると、利点は何ですか

(define (action kind)
    (lambda (a b)
        (kind a b)))

とは対照的に

(define (action kind a b)
    (kind a b))

より多くのコードしか表示されず、追加の柔軟性はありません...

4

10 に答える 10

24

カリー化された関数の有効な使い方の 1 つは、コード量の削減です。

3 つの関数を考えてみましょう。そのうちの 2 つはほとんど同じです。

(define (add a b)
  (action + a b))

(define (mul a b)
  (action * a b))

(define (action kind a b)
  (kind a b))

コードが を呼び出す場合はadd、次にactionkindで呼び出します+。と同じmulです。

これらの関数を、利用可能な多くの命令型の一般的な言語で行うように定義しました (それらの一部には、関数の世界で通常見られるラムダ、カリー化、およびその他の機能が含まれています。これらはすべて非常に便利なためです)。

All addand do は、への呼び出しを適切な でsumラップしています。ここで、これらの関数のカリー化された定義を検討してください。actionkind

(define add-curried
  ((curry action) +))

(define mul-curried
  ((curry action) *))

それらはかなり短くなりました。action1 つの引数のみを渡して関数kindをカリー化し、残りの 2 つの引数を受け入れるカリー化された関数を取得しました。

このアプローチにより、記述するコードが少なくなり、保守性が高くなります。

action関数がすぐに書き換えられて、さらに 3 つの引数を受け入れることを想像してみてください。カリー化しないと、 and の実装を書き直す必要がありaddますmul

(define (action kind a b c d e)
  (kind a b c d e))

(define (add a b c d e)
  (action + a b c d e))

(define (mul a b c d e)
  (action * a b c d e))

しかし、カリー化によって、厄介でエラーが発生しやすい作業から解放されました。呼び出し元の関数が に渡される必要な量の引数を提供するため、関数内のシンボルを書き換える必要はまったくadd-curriedありません。mul-curriedaction

于 2011-02-03T16:47:40.193 に答える
11

コードを読みやすくすることができます。次の 2 つの Haskell スニペットを検討してください。

lengths :: [[a]] -> [Int]
lengths xs = map length xs

lengths' :: [[a]] -> [Int]
lengths' = map length

使用しない変数に名前を付けるのはなぜですか?

カリー化された関数は、次のような状況でも役立ちます。

doubleAndSum ys = map (\xs -> sum (map (*2) xs) ys

doubleAndSum' = map (sum . map (*2))

これらの余分な変数を削除すると、コードが読みやすくなり、xs と ys を頭の中で明確にしておく必要がなくなります。

HTH。

于 2011-02-03T17:28:27.057 に答える
4

カリー化は専門として見ることができます。いくつかのデフォルトを選択し、ユーザー (おそらく自分自身) に特化した、より表現力豊かな関数を残します。

于 2011-02-03T17:07:39.240 に答える
3

カリー化は、定義できる関数が単項関数のみであるという条件で、一般的な n 項関数を処理する従来の方法だと思います。

たとえば、ラムダ計算 (関数型プログラミング言語が由来する) では、1 つの変数の抽象化 (FPL の単項関数に変換される) しかありません。ラムダ計算に関しては、n 項関数のケースを実際に処理する必要がないため、そのような形式主義について証明する方が簡単だと思います (カリー化により、多数の単項関数で任意の n 項関数を表すことができるため)。 .

(この決定の実際的な影響については、他の人がすでに説明しているので、ここでやめておきます。)

于 2011-02-04T02:41:58.657 に答える
2

all :: (a -> Bool) -> [a] -> Boolカリー化された述語での使用。

all (`elem` [1,2,3]) [0,3,4,5]

Haskell 中置演算子はどちらの側でもカリー化できるため、elem関数のニードルまたはコンテナー側 (is-element-of) を簡単にカリー化できます。

于 2011-10-04T22:44:23.873 に答える
2

複数のパラメーターを取る関数を直接構成することはできません。関数合成は、関数型プログラミングの重要な概念の 1 つです。Currying 手法を使用することで、複数のパラメーターを取る関数を構成できます。

于 2017-05-11T12:44:28.887 に答える
1

@Francescoの回答に例を追加したいと思います。

ここに画像の説明を入力

于 2015-04-09T09:28:51.970 に答える
0

したがって、小さなラムダでボイラープレートを増やす必要はありません。

于 2011-02-03T16:07:54.737 に答える
0

クロージャの作成は非常に簡単です。時々、SRFI-26 を使用します。本当にかわいいです。

于 2011-02-03T16:15:27.883 に答える
0

カリー化自体がシンタックス シュガーです。シンタックス シュガーとは、簡単にしたいことのすべてです。たとえば、C は、インクリメントのようなアセンブリ言語で「安価」で簡単な命令を作成したいと考えているため、インクリメント用のシンタックス シュガーである ++ 表記を使用しています。

 t = x + y
 x = x + 1

t = x++ + y に置き換えられます

関数型言語には、次のようなものを簡単に含めることができます。

f(x,y,z) = abc 
g(r,s)(z) = f(r,s,z). 
h(r)(s)(z) = f(r,s,z)

代わりに、すべて自動です。これにより、特定の r0、s0 (つまり、特定の値) によってバインドされた ag を 1 つの変数関数として渡すことができます。

たとえば、サブがブール値に評価される2つの変数の関数であり、リストが任意のリストであるソートサブリストを取るperlのソート関数を考えてみましょう。

当然のことながら、Perl で比較演算子 (<=>) を使用し、sortordinal = sort (<=>) を使用して、sortordinal がリストで機能するようにします。これを行うには、カリー化された関数になるように並べ替えます。
実際、ある種のリストは、Perl では正確にこのように定義されています。

要するに、カリー化はファーストクラスの関数をより自然にするための砂糖です。

于 2011-03-09T03:18:32.743 に答える