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


81

Scalaでは、このような関数を書くことができます(カレー?)

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

curriedFunc2つのパラメーターリストを持つ上記の関数定義と、1つのパラメーターリストに複数のパラメーターを持つ関数の違いは何ですか。

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

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

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

他に違いはありますか?

回答:


88

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

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

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

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

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

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

もう1つの利点は、デフォルトの引数値を定義するために前の引数リストの引数を参照できることです(ただし、単一のリストではそれができないという欠点もあります:)

// 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、およびextemporeにあります。

まず、複数の変数引数を持つことができます:

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

2
それが最も投票された回答なので、質問のタイトルはもはやその回答に対応していないと思います。タイトルを「Scalaが複数のパラメーターリストとリストごとに複数のパラメーターの両方を提供するのはなぜですか?」に変更する必要があると思います。つまり、サンプルによってすでにstackoverflow.com/questions/4684185/…とマージされています。
Jacek Laskowski 2013

42

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

例えば:

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

これを吸収するために、この簡単な例を取り上げました。これにより、実際にはデフォルトのパラメーターがはるかに便利になります。ありがとう!
マイクマクファーランド

:私はそれを呼び出す方法を把握するために5分を過ごした以外は、実施例グッドg(1)()戻っ3. g(1)(2)戻り5.
サピエンス

19

それがポイントです、カレーとカレーなしの形は同等です!他の人が指摘しているように、状況に応じて、どちらかの形式の方が構文的に便利であり、それが一方を他方よりも好む唯一の理由です。

Scalaにカレー関数を宣言するための特別な構文がなかったとしても、それらを構築できることを理解することが重要です。関数を返す関数を作成できるようになると、これは数学的な必然性にすぎません。

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

Scalaの多くの機能と同様に、これはとにかく可能であるが、少し冗長性を備えた何かを行うための構文上の便利さです。


4

2つの形式は同型です。主な違いは、少なくともScalaでは、カレー関数は部分的に適用しやすいのに対し、カレーなし関数の構文は少し優れていることです。


2
例はカレー関数ではないと以前に言われていませんか?カリー化された関数には引数が1つしかないことを理解しており、すべての引数が閉じられた本体ができるまで、引数が1つだけの関数を返す可能性があります。私が間違っている?
Jacek Laskowski 2013
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.