Frage Was ist der Unterschied zwischen mehreren Parameterlisten und mehreren Parametern pro Liste in Scala?


In Scala kann man solche Funktionen schreiben (Curry?)

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

Was ist der Unterschied zwischen den oben genannten? curriedFunc Funktionsdefinition mit zwei Parameterlisten und Funktionen mit mehreren Parametern in einer einzigen Parameterliste:

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

Aus mathematischer Sicht ist dies (curriedFunc(x))(y) und curriedFunc(x,y) aber ich kann schreiben def sum(x) (y) = x + y und das Gleiche wird sein def sum2(x, y) = x + y

Ich kenne nur einen Unterschied - das sind teilweise angewandte Funktionen. Aber beide Wege sind für mich gleichwertig.

Gibt es noch andere Unterschiede?


76
2017-07-23 20:48


Ursprung


Antworten:


Streng genommen handelt es sich hierbei nicht um eine Curry-Funktion, sondern um eine Methode mit mehreren Argumentlisten, die allerdings wie eine Funktion aussieht.

Wie Sie bereits sagten, erlauben die Listen mit mehreren Argumenten, dass die Methode anstelle einer partiell angewendeten Funktion verwendet wird. (Entschuldigung für die allgemein dummen Beispiele, die ich benutze)

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

Ein weiterer Vorteil ist, dass Sie geschweifte Klammern anstelle von Klammern verwenden können, die gut aussehen, wenn die zweite Argumentliste aus einer einzigen Funktion oder Thunk besteht. Z.B.

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

gegen

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

Oder für den Thunk:

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

Ein anderer Vorteil ist, dass Sie sich auf Argumente einer vorherigen Argumentliste beziehen können, um Standard-Argumentwerte zu definieren (obwohl Sie auch sagen könnten, dass es ein Nachteil ist, dass Sie das nicht in einer einzelnen Liste machen können :)

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

Schließlich gibt es drei andere Anwendungen in einer Antwort auf verwandte Post Warum bietet Scala sowohl mehrere Parameterlisten als auch mehrere Parameter pro Liste? . Ich werde sie hier nur kopieren, aber der Verdienst geht an Knut Arne Vedaa, Kevin Wright und extempore.

Erstens: Sie können mehrere var args haben:

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

... was in einer einzigen Argumentliste nicht möglich wäre.

Zweitens unterstützt es die Typinferenz:

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

Und zuletzt, das ist die einzige Möglichkeit, wie Sie implizite und nicht implizite Argumente haben können implicit ist ein Modifikator für eine ganze Argumentliste:

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

81
2017-07-23 23:21



Es gibt einen weiteren Unterschied, der nicht durch 0 ausgezeichnet wurde Antworten: Standardparameter Ein Parameter aus einer Parameterliste kann verwendet werden, wenn der Standardwert in einer anderen Parameterliste berechnet wird, jedoch nicht in derselben.

Beispielsweise:

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

39
2017-07-26 02:57



Das ist der springende Punkt, dass die Curry- und Uncurried-Formen gleichwertig sind! Wie andere gezeigt haben, kann die eine oder andere Form sein syntaktisch bequemer mit der Situation zu arbeiten, und das ist der einzige Grund, eine über die andere zu bevorzugen.

Es ist wichtig zu verstehen, dass, selbst wenn Scala keine spezielle Syntax zum Deklarieren von Curry-Funktionen hätte, sie trotzdem konstruiert werden könnten; Dies ist nur eine mathematische Zwangsläufigkeit, sobald Sie die Fähigkeit haben, Funktionen zu erstellen, die Funktionen zurückgeben.

Stellen Sie sich vor, dass das def foo(a)(b)(c) = {...} Syntax existierte nicht. Dann könntest du immer noch genau das Gleiche erreichen: def foo(a) = (b) => (c) => {...}.

Wie bei vielen Funktionen in Scala ist dies nur eine syntaktische Bequemlichkeit, um etwas zu tun, das sowieso möglich wäre, aber mit etwas mehr Ausführlichkeit.


18
2017-07-23 23:18



Die zwei Formen sind isomorph. Der Hauptunterschied besteht darin, dass Curry-Funktionen teilweise einfacher anzuwenden sind, während Nicht-Curry-Funktionen, zumindest in Scala, eine etwas nettere Syntax haben.


4
2017-07-23 20:55