1. ホーム
  2. scala

[解決済み] ScalaでCurryする2つの方法、それぞれのユースケースは?

2023-05-30 06:37:07

質問

私は、以下のような議論をしています。 複数のパラメータ・リスト という記述があります。の2つの方法があることに気がつきました. カーリング という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)_

スタイル ガイドでは、明らかに異なるのに、これらが同じであるかのように誤って解釈しています。このガイドでは、作成された curried 関数について指摘しようとしており、2 番目の形式は "by-the-book" currying ではないものの、最初の形式と非常に似ています (しかし、間違いなく _ )

これらのフォームを使用している人たちから、どのような場合に一方のフォームを他方よりも使用するかについて、コンセンサスは得られていますか?

どのように解決するのですか?

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

型推論用

複数のパラメータセクションを持つメソッドは、最初のセクションのパラメータを使用して、後続のセクションの引数に期待される型を提供する型引数を推論することにより、局所的な型推論を支援するために使用することができます。 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のために

複数のパラメータを持つセクションメソッドのもう一つの使い方は、言語の構成要素のように見える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 リファレンスの Scala 2.0 の変更ノートを参照してください。

Curried Functions

曲線関数(または単に関数を返す関数)は、N個の引数リストに対してより簡単に適用することができます。

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

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

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

多段階計算

curried関数は他にどこで役に立つのでしょうか?よく出てくるパターンを紹介しましょう。

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!
}