web-dev-qa-db-ja.com

Scalaの複数のパラメーターリストとリストごとの複数のパラメーターの違いは何ですか?

Scalaでは、このような(カリー化された)関数を記述できます)

_def curriedFunc(arg1: Int) (arg2: String) = { ... }
_

上記のcurriedFunc関数定義と2つのパラメーターリストの違いは何ですか?

_def curriedFunc(arg1: Int, arg2: String) = { ... }
_

数学的な観点から、これは_(curriedFunc(x))(y)_とcurriedFunc(x,y)ですが、def sum(x) (y) = x + yと書くこともでき、同じことがdef sum2(x, y) = x + yになります

私は唯一の違いを知っています-これは部分的に適用された関数です。しかし、どちらの方法も同じです。

他に違いはありますか?

78
den bardadym

厳密に言えば、これはカレー関数ではなく、確かに関数のように見えますが、複数の引数リストを持つメソッドです。

あなたが言ったように、複数の引数リストにより、メソッドを部分的に適用された関数の代わりに使用できます。 (私が使用する一般的に愚かな例では申し訳ありません)

object NonCurr {
  def tabulate[A](n: Int, fun: Int => A) = IndexedSeq.tabulate(n)(fun)
}

NonCurr.tabulate[Double](10, _)            // not possible
val x = IndexedSeq.tabulate[Double](10) _  // possible. x is Function1 now
x(math.exp(_))                             // complete the application

別の利点は、2番目の引数リストが単一の関数またはサンクで構成されている場合、カッコの代わりに中括弧を使用できることです。例えば。

NonCurr.tabulate(10, { i => val j = util.Random.nextInt(i + 1); i - i % 2 })

versus

IndexedSeq.tabulate(10) { i =>
  val j = util.Random.nextInt(i + 1)
  i - i % 2
}

またはサンクの場合:

IndexedSeq.fill(10) {
  println("debug: operating the random number generator")
  util.Random.nextInt(99)
}

別の利点は、デフォルトの引数値を定義するために前の引数リストの引数を参照できることです(単一のリストでそれを実行できないことは不利であると言うこともできますが)。

// again I'm not very creative with the example, so forgive me
def doSomething(f: Java.io.File)(modDate: Long = f.lastModified) = ???

最後に、関連する投稿への回答には他に3つのアプリケーションがあります Scalaは複数のパラメータリストとリストごとに複数のパラメータの両方を提供するのはなぜですか? 。ここにそれらをコピーするだけです、しかしクレジットはKnut Arne Vedaa、Kevin Wright、そして臨時のものです。

最初に、あなたは複数の変数引数を持つことができます:

def foo(as: Int*)(bs: Int*)(cs: Int*) = as.sum * bs.sum * cs.sum

...これは単一の引数リストでは不可能です。

次に、型推論を支援します。

def foo[T](a: T, b: T)(op: (T,T) => T) = op(a, b)
foo(1, 2){_ + _}   // compiler can infer the type of the op function

def foo2[T](a: T, b: T, op: (T,T) => T) = op(a, b)
foo2(1, 2, _ + _)  // compiler too stupid, unfortunately

そして最後に、implicitは引数リスト全体の修飾子であるため、これは暗黙的および非暗黙的な引数を持つことができる唯一の方法です。

def gaga [A](x: A)(implicit mf: Manifest[A]) = ???   // ok
def gaga2[A](x: A, implicit mf: Manifest[A]) = ???   // not possible
84
0__

0__の優れた answer :デフォルトパラメータではカバーされなかった別の違いがあります。あるパラメーターリストのパラメーターは、別のパラメーターリストのデフォルトを計算するときに使用できますが、同じパラメーターリストでは使用できません。

例えば:

def f(x: Int, y: Int = x * 2) = x + y // not valid
def g(x: Int)(y: Int = x * 2) = x + y // valid
41

それが肝心なのは、カレーの形とカレーの形が同等であることです!他の人が指摘したように、どちらか一方の形式は、状況に応じて作業するのに構文的により便利な場合があり、それが他の形式を優先する唯一の理由です。

Scalaにカリー化された関数を宣言するための特別な構文がなかったとしても、それらを構築することができます。これは、返される関数を作成する機能があれば、数学的に不可避であることを理解することが重要です。関数。

これを示すために、def foo(a)(b)(c) = {...}構文が存在しなかったと想像してください。そうすれば、def foo(a) = (b) => (c) => {...}のようにまったく同じことを実現できます。

Scalaの多くの機能と同様に、これはとにかく可能なことを行うための構文上の便宜ですが、少し冗長性があります。

19
Tom Crockett

2つの形式は同型です。主な違いは、カリー化された関数は部分的に適用する方が簡単ですが、カリー化されていない関数は、少なくともScalaでは構文が少し優れています。

4
hammar