Der semantische Unterschied wurde in der Antwort von Plasty Grove ziemlich gut erklärt .
In Bezug auf die Funktionalität scheint es jedoch keinen großen Unterschied zu geben. Schauen wir uns einige Beispiele an, um dies zu überprüfen. Erstens eine normale Funktion:
scala> def modN(n: Int, x: Int): Boolean = ((x % n) == 0)
scala> modN(5, _ : Int)
res0: Int => Boolean = <function1>
Wir bekommen also eine teilweise angewendete <function1>
, die eine nimmt Int
, weil wir ihr bereits die erste ganze Zahl gegeben haben. So weit, ist es gut. Nun zum Curry:
scala> def modNCurried(n: Int)(x: Int): Boolean = ((x % n) == 0)
Mit dieser Notation würden Sie naiv erwarten, dass Folgendes funktioniert:
scala> modNCurried(5)
<console>:9: error: missing arguments for method modN;
follow this method with `_' if you want to treat it as a partially applied function
modNCurried(5)
Die Notation mit mehreren Parameterlisten scheint also nicht sofort eine Curry-Funktion zu erstellen (vermutlich, um unnötigen Overhead zu vermeiden), sondern wartet darauf, dass Sie explizit angeben, dass Sie sie als Curry wünschen (die Notation hat auch einige andere Vorteile ):
scala> modNCurried(5) _
res24: Int => Boolean = <function1>
Welches ist genau das gleiche, was wir vorher bekommen haben, also kein Unterschied hier, außer der Notation. Ein anderes Beispiel:
scala> modN _
res35: (Int, Int) => Boolean = <function2>
scala> modNCurried _
res36: Int => (Int => Boolean) = <function1>
Dies zeigt, wie das teilweise Anwenden einer "normalen" Funktion zu einer Funktion führt, die alle Parameter akzeptiert, während das teilweise Anwenden einer Funktion mit mehreren Parameterlisten eine Funktionskette erzeugt, eine pro Parameterliste , die alle eine neue Funktion zurückgibt:
scala> def foo(a:Int, b:Int)(x:Int)(y:Int): Int = a * b + x - y
scala> foo _
res42: (Int, Int) => Int => (Int => Int) = <function2>
scala> res42(5)
<console>:10: error: not enough arguments for method apply: (v1: Int, v2: Int)Int => (Int => Int) in trait Function2.
Unspecified value parameter v2.
Wie Sie sehen können, foo
hat die erste Funktion in der Curry-Kette zwei Parameter , da die erste Parameterliste zwei Parameter enthält.
Zusammenfassend lässt sich sagen, dass teilweise angewendete Funktionen sich in Bezug auf die Funktionalität nicht wirklich von Curry-Funktionen unterscheiden. Dies lässt sich leicht überprüfen, da Sie jede Funktion in eine Curry-Funktion umwandeln können:
scala> (modN _).curried
res45: Int => (Int => Boolean) = <function1
scala> modNCurried _
res46: Int => (Int => Boolean) = <function1>
Post Scriptum
Hinweis: Der Grund dafür, dass Ihr Beispiel println(filter(nums, modN(2))
ohne den Unterstrich danach funktioniert, modN(2)
scheint darin zu liegen, dass der Scala-Compiler diesen Unterstrich einfach als Annehmlichkeit für den Programmierer annimmt.
Ergänzung: Wie @asflierl richtig hervorgehoben hat, scheint Scala nicht in der Lage zu sein, auf den Typ zu schließen, wenn teilweise "normale" Funktionen angewendet werden:
scala> modN(5, _)
<console>:9: error: missing parameter type for expanded function ((x$1) => modN(5, x$1))
Diese Informationen sind für Funktionen verfügbar, die mit der Notation mehrerer Parameterlisten geschrieben wurden:
scala> modNCurried(5) _
res3: Int => Boolean = <function1>
Diese Antwort zeigt, wie nützlich dies sein kann.