84

私が管理しているScalaスタイルガイドで複数のパラメーターリストについて話し合っています。カリー化には2つの方法があることに気づきましたが、その使用例は何でしょうか。

def add(a:Int)(b:Int) = {a + b}
// Works
add(5)(6)
// Doesn't compile
val f = add(5)
// Works
val f = add(5)_
f(10) // yields 15

def add2(a:Int) = { b:Int => a + b }
// Works
add2(5)(6)
// Also works
val f = add2(5)
f(10) // Yields 15
// Doesn't compile
val f = add2(5)_

スタイルガイドは、明らかにそうではないのに、これらは同じであると誤って示唆しています。このガイドは、作成されたカリー化関数について説明しようとしています。2番目の形式は「本による」カリー化ではありませんが、最初の形式と非常によく似ています(ただし、必要がないため、間違いなく使いやすいです)。_)_

これらのフォームを使用するものから、一方のフォームをもう一方のフォームよりもいつ使用するかについてのコンセンサスは何ですか?

4

3 に答える 3

139

複数のパラメータリストメソッド

型推論の場合

複数のパラメーターセクションを持つメソッドを使用して、最初のセクションのパラメーターを使用して、後続のセクションの引数に期待される型を提供する型引数を推論することにより、ローカル型の推論を支援できます。foldLeft標準ライブラリには、この標準的な例があります。

def foldLeft[B](z: B)(op: (B, A) => B): B

List("").foldLeft(0)(_ + _.length)

これが次のように書かれている場合:

def foldLeft[B](z: B, op: (B, A) => B): B

より明示的なタイプを提供する必要があります。

List("").foldLeft(0, (b: Int, a: String) => a + b.length)
List("").foldLeft[Int](0, _ + _.length)

流暢なAPIの場合

複数のパラメーターセクションメソッドのもう1つの使用法は、言語構造のように見えるAPIを作成することです。呼び出し元は、括弧の代わりに中括弧を使用できます。

def loop[A](n: Int)(body: => A): Unit = (0 until n) foreach (n => body)

loop(2) {
   println("hello!")
}

N個の引数リストをM個のパラメーターセクションを持つメソッドに適用すると、N <Mは、明示的に_、または暗黙的に、期待されるタイプの。を使用して関数に変換できますFunctionN[..]。これは安全機能です。背景については、ScalaリファレンスのScala2.0の変更に関する注意事項を参照してください。

カレー関数

カレー関数(または単に関数を返す関数)は、N個の引数リストに簡単に適用できます。

val f = (a: Int) => (b: Int) => (c: Int) => a + b + c
val g = f(1)(2)

このマイナーな便利さは時々価値があります。ただし、関数をパラメトリック型にすることはできないため、場合によってはメソッドが必要になることに注意してください。

2番目の例はハイブリッドです。関数を返す1つのパラメーターセクションメソッドです。

多段階計算

カレー関数は他にどこで役に立ちますか?いつも出てくるパターンは次のとおりです。

def v(t: Double, k: Double): Double = {
   // expensive computation based only on t
   val ft = f(t)

   g(ft, k)
}

v(1, 1); v(1, 2);

結果をどのように共有できf(t)ますか?一般的な解決策は、ベクトル化されたバージョンを提供することですv

def v(t: Double, ks: Seq[Double]: Seq[Double] = {
   val ft = f(t)
   ks map {k => g(ft, k)}
}

ぶさいくな!関係のない懸念を絡ませました-g(f(t), k)一連の。の計算とマッピングks

val v = { (t: Double) =>
   val ft = f(t)
   (k: Double) => g(ft, k)       
}
val t = 1
val ks = Seq(1, 2)
val vs = ks map (v(t))

関数を返すメソッドを使用することもできます。この場合、もう少し読みやすくなります。

def v(t:Double): Double => Double = {
   val ft = f(t)
   (k: Double) => g(ft, k)       
}

しかし、複数のパラメーターセクションを持つメソッドで同じことを行おうとすると、行き詰まります。

def v(t: Double)(k: Double): Double = {
                ^
                `-- Can't insert computation here!
}
于 2011-02-06T22:22:42.330 に答える
16

カレーは関数のみで、メソッドはカレーできません。はメソッドであるため、関数への変換を強制addする必要があります。関数を返すので、これは不要であるだけでなく、ここでは意味がありません。_add2_

メソッドと関数の違いを考えると(たとえば、JVMの観点から)、Scalaはそれらの間の境界線を曖昧にし、ほとんどの場合「正しいこと」を実行するのにかなり良い仕事をします、違いがあり、場合によっては必要なだけです。それについて知るために。

于 2011-02-06T18:10:21.023 に答える
5

2つのdef add(a: Int)(b: Int): Intパラメーターを使用してメソッドを定義するだけで、それらの2つのパラメーターのみが2つのパラメーターリストにグループ化されることを追加すると、違いを理解するのに役立つと思います(他のコメントでその結果を参照してください)。実際、その方法はJava(Scalaではありません!)に関する限りです。を書くとき、それは単なる関数リテラルであり、の短い形式です。一方、パラメータを1つだけ持つメソッドを定義すると、Javaの場合はになります。Scalaで書くとき、それは(関数リテラルではなく)単なるメソッド呼び出しです。int add(int a, int a)add(5)_{ b: Int => add(1)(b) }add2(a: Int) = { b: Int => a + b }scala.Function add2(int a)add2(1)

また、すべてのパラメーターをすぐに指定した場合addよりも(潜在的に)オーバーヘッドが少ないことにも注意してください。JVMレベルで変換するのadd2と同様に、オブジェクトは作成されません。一方、は、最初にを囲むオブジェクトを作成し、次にそれを呼び出します。add(5)(6)add(5, 6)Functionadd2(5)(6)Function5apply(6)

于 2011-02-07T16:10:09.640 に答える