84

カリー化と部分的に適用された関数とは何かについて、ここでいくつかの質問があることを認識していますが、それらがどのように異なるかについて質問しています。簡単な例として、偶数を見つけるためのカリー化された関数を次に示します。

def filter(xs: List[Int], p: Int => Boolean): List[Int] =
   if (xs.isEmpty) xs
   else if (p(xs.head)) xs.head :: filter(xs.tail, p)
   else filter(xs.tail, p)

def modN(n: Int)(x: Int) = ((x % n) == 0)

したがって、これを使用するには、次のように記述できます。

val nums = List(1,2,3,4,5,6,7,8)
println(filter(nums, modN(2))

戻り値: List(2,4,6,8). しかし、次の方法でも同じことができることがわかりました。

def modN(n: Int, x: Int) = ((x % n) == 0)

val p = modN(2, _: Int)
println(filter(nums, p))

また、次を返します: List(2,4,6,8).

私の質問は、この 2 つの主な違いは何ですか? また、いつどちらを使用しますか? これは、一方が他方よりも優先して使用される理由を示すには、単純すぎる例ですか?

4

5 に答える 5

88

意味の違いは、PlastyGroveによってリンクされた回答でかなりよく説明されています。

ただし、機能面ではそれほど違いはないようです。それを確認するためにいくつかの例を見てみましょう。まず、通常の機能:

scala> def modN(n: Int, x: Int): Boolean = ((x % n) == 0)
scala> modN(5, _ : Int)
res0: Int => Boolean = <function1>

したがって、最初の整数をすでに指定しているため、<function1>をとる部分的に適用されます。Intここまでは順調ですね。今カリー化に:

scala> def modNCurried(n: Int)(x: Int): Boolean = ((x % n) == 0)

この表記では、次のことが機能することを素朴に期待できます。

scala> modNCurried(5)
<console>:9: error: missing arguments for method modN;
follow this method with `_' if you want to treat it as a partially applied function
          modNCurried(5)

したがって、複数パラメーターリスト表記は、実際にはすぐにカリー化された関数を作成するようには見えませんが(おそらく、不要なオーバーヘッドを回避するため)、カリー化することを明示的に示すのを待ちます(この表記には他にもいくつかの利点があります)。

scala> modNCurried(5) _
res24: Int => Boolean = <function1>

これは以前に取得したものとまったく同じなので、表記を除いてここでは違いはありません。もう一つの例:

scala> modN _
res35: (Int, Int) => Boolean = <function2>

scala> modNCurried _
res36: Int => (Int => Boolean) = <function1>

これは、「通常の」関数を部分的に適用すると、すべてのパラメーターを受け取る関数になるのに対し、複数のパラメーターリストを持つ関数を部分的に適用すると、パラメーターリストごとに1つずつ、すべて新しい関数を返す一連の関数が作成されることを示しています。

scala> def foo(a:Int, b:Int)(x:Int)(y:Int): Int = a * b + x - y
scala> foo _
res42: (Int, Int) => Int => (Int => Int) = <function2>

scala> res42(5)
<console>:10: error: not enough arguments for method apply: (v1: Int, v2: Int)Int => (Int => Int) in trait Function2.
Unspecified value parameter v2.

ご覧のとおり、の最初のパラメーターリストにfooは2つのパラメーターがあるため、カレーチェーンの最初の関数には2つのパラメーターがあります。


要約すると、部分的に適用された関数は、機能の点でカレー関数と実際には異なりません。これは、任意の関数をカレー関数に変換できることを考えると、簡単に確認できます。

scala> (modN _).curried
res45: Int => (Int => Boolean) = <function1

scala> modNCurried _
res46: Int => (Int => Boolean) = <function1>

ポストスクリプトム

注:println(filter(nums, modN(2))例が後にアンダースコアなしで機能する理由はmodN(2)、Scalaコンパイラーがプログラマーの便宜のためにアンダースコアを単に想定しているためと思われます。


追加: @asflierlが正しく指摘しているように、「通常の」関数を部分的に適用すると、Scalaは型を推測できないようです。

scala> modN(5, _)
<console>:9: error: missing parameter type for expanded function ((x$1) => modN(5, x$1))

その情報は、複数のパラメーターリスト表記を使用して記述された関数で利用できます。

scala> modNCurried(5) _
res3: Int => Boolean = <function1>

この回答は、これがどのように非常に役立つかを示しています。

于 2013-01-14T09:42:49.633 に答える
19

カリー化とはタプルを扱うことです:タプル引数を取る関数を n 個の別々の引数を取る関数に変換し、その逆も同様です。これを覚えておくことは、カリー化を明確にサポートしていない言語であっても、カリーと部分適用を区別するための鍵となります。

curry :: ((a, b) -> c) -> a -> b -> c 
   -- curry converts a function that takes all args in a tuple
   -- into one that takes separate arguments

uncurry :: (a -> b -> c) -> (a, b) -> c
   -- uncurry converts a function of separate args into a function on pairs.

部分適用とは、一部の引数に関数を適用し、残りの引数に対して新しい関数を生成する機能です

カリー化とはタプルを使った変換だと考えれば覚えやすいでしょう。

デフォルトでカリー化されている言語 (Haskell など) では、違いは明らかです。タプルで引数を渡すには、実際に何かを行う必要があります。しかし、Scala を含む他のほとんどの言語は、デフォルトでカリー化されていません。すべての引数はタプルとして渡されるため、カリー/アンカリーはあまり役に立たず、あまり明白ではありません。カリー化された関数を簡単に表現できないという理由だけで、人々は部分適用とカリー化を同じものと考えるようになります!

于 2013-01-14T14:25:15.663 に答える
2

多変数関数:

def modN(n: Int, x: Int) = ((x % n) == 0)

カリー化 (またはカリー化された関数):

def modNCurried(n: Int)(x: Int) = ((x % n) == 0)

したがって、カリー化に匹敵する部分適用機能ではありません。それが多変数関数です。部分的に適用された関数に匹敵するのは、カリー化された関数の呼び出し結果です。これは、部分的に適用された関数と同じパラメーター リストを持つ関数です。

于 2014-07-14T20:49:00.997 に答える
0

最後の点を明確にするために

追加: @asflierl が正しく指摘しているように、「通常の」関数を部分的に適用すると、Scala は型を推測できないようです。

すべてのパラメーターがワイルドカードである場合、Scala は型を推測できますが、一部が指定されていて、一部が指定されていない場合はそうではありません。

scala> modN(_,_)
res38: (Int, Int) => Boolean = <function2>

scala> modN(1,_)
<console>:13: error: missing parameter type for expanded function ((x$1) => modN(1, x$1))
       modN(1,_)
              ^
于 2016-08-11T18:00:06.343 に答える