Mir ist klar, dass es hier einige Fragen dazu gibt, was Currying und teilweise angewandte Funktionen sind, aber ich frage, wie sie sich unterscheiden. Als einfaches Beispiel finden Sie hier eine Curry-Funktion zum Finden gerader Zahlen:
def filter(xs: List[Int], p: Int => Boolean): List[Int] =
if (xs.isEmpty) xs
else if (p(xs.head)) xs.head :: filter(xs.tail, p)
else filter(xs.tail, p)
def modN(n: Int)(x: Int) = ((x % n) == 0)
Sie könnten also Folgendes schreiben, um dies zu verwenden:
val nums = List(1,2,3,4,5,6,7,8)
println(filter(nums, modN(2))
was zurückgibt : List(2,4,6,8)
. Aber ich habe festgestellt, dass ich das Gleiche auf diese Weise tun kann:
def modN(n: Int, x: Int) = ((x % n) == 0)
val p = modN(2, _: Int)
println(filter(nums, p))
was auch zurückgibt : List(2,4,6,8)
.
Meine Frage ist also, was ist der Hauptunterschied zwischen den beiden und wann würden Sie einen über den anderen verwenden? Ist dies ein zu einfaches Beispiel, um zu zeigen, warum eines über das andere verwendet wird?
Antworten:
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:
Wir bekommen also eine teilweise angewendete
<function1>
, die eine nimmtInt
, weil wir ihr bereits die erste ganze Zahl gegeben haben. So weit, ist es gut. Nun zum Curry:Mit dieser Notation würden Sie naiv erwarten, dass Folgendes funktioniert:
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 ):
Welches ist genau das gleiche, was wir vorher bekommen haben, also kein Unterschied hier, außer der Notation. Ein anderes Beispiel:
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:
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:
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:
Diese Informationen sind für Funktionen verfügbar, die mit der Notation mehrerer Parameterlisten geschrieben wurden:
Diese Antwort zeigt, wie nützlich dies sein kann.
quelle
Currying hat mit Tupeln zu tun: Verwandeln einer Funktion, die ein Tupelargument verwendet, in eine Funktion, die n separate Argumente akzeptiert, und umgekehrt . Denken Sie daran, dass dies der Schlüssel zur Unterscheidung zwischen Curry und Teilanwendung ist, selbst in Sprachen, die Curry nicht sauber unterstützen.
Teilanwendung ist die Fähigkeit, eine Funktion auf einige Argumente anzuwenden, wodurch eine neue Funktion für die verbleibenden Argumente erhalten wird .
Es ist leicht zu merken, wenn Sie nur denken, dass Curry die Transformation ist, die mit Tupeln zu tun hat.
In Sprachen, die standardmäßig verwendet werden (wie Haskell), ist der Unterschied klar: Sie müssen tatsächlich etwas tun, um Argumente in einem Tupel zu übergeben. Die meisten anderen Sprachen, einschließlich Scala, werden standardmäßig ohne Eile verwendet. Alle Argumente werden als Tupel übergeben, sodass Curry / Uncurry weitaus weniger nützlich und weniger offensichtlich ist. Und am Ende denken die Leute sogar, dass teilweises Auftragen und Currying dasselbe sind - nur weil sie Curry-Funktionen nicht einfach darstellen können!
quelle
Multivariable Funktion:
Currying (oder die Curry-Funktion):
Es ist also keine teilweise angewendete Funktion, die mit Curry vergleichbar ist. Es ist die multivariable Funktion. Was mit einer teilweise angewendeten Funktion vergleichbar ist, ist das Aufrufergebnis einer Curry-Funktion, bei der es sich um eine Funktion mit derselben Parameterliste handelt, die die teilweise angewendete Funktion hat.
quelle
Nur um den letzten Punkt zu verdeutlichen
Scala kann Typen ableiten, wenn alle Parameter Platzhalter sind, aber nicht, wenn einige von ihnen angegeben sind und einige nicht.
quelle
Die beste Erklärung, die ich bisher finden konnte: https://dzone.com/articles/difference-between-currying-amp-partially-applied
quelle