2011-01-15 11 views
6

In Scala kann ich eine Funktion mit zwei Parameterlisten definieren.Scala currying durch verschachtelte Funktionen oder durch mehrere Parameterlisten

def myAdd(x :Int)(y :Int) = x + y 

Dies macht es einfach, eine teilweise angewandte Funktion zu definieren.

val plusFive = myAdd(5) _ 

Aber ich kann etwas Ähnliches erreichen, indem ich eine verschachtelte Funktion definiere und zurückgebe.

def myOtherAdd(x :Int) = { 
    def f(y :Int) = x + y 
    f _ 
    } 

Kosmetisch, ich habe den Unterstrich verschoben, aber das fühlt sich immer noch an wie Curry.

Welche Kriterien sollte ich verwenden, um einen Ansatz gegenüber dem anderen zu bevorzugen?

Antwort

6

Sie könnten auch dies tun:

def yetAnotherAdd(x: Int) = x + (_: Int) 

Sie sollten die API wählen auf Vorsatz beruhen. Der Hauptgrund in Scala, mehrere Parameterlisten zu haben, ist es, Inferenz zu schreiben. Zum Beispiel:

def f[A](x: A)(f: A => A) = ... 
f(5)(_ + 5) 

Man kann es auch verwenden, um mehrere Varargs zu haben, aber ich habe Code nie so gesehen. Und natürlich gibt es die Notwendigkeit für die implizite Parameterliste, aber das ist ziemlich viel anderes.

Jetzt gibt es viele Möglichkeiten, wie Sie Funktionen Funktionen zurückgeben können, die ziemlich genau was currying tut. Sie sollten sie verwenden, wenn das API als eine Funktion gedacht ist, die eine Funktion zurückgibt.

Ich denke, es ist schwierig, genauer als das zu bekommen.

+0

Danke, Daniel. Ihr Kommentar zu Typinferenz als Motivation für mehrere Parameterlisten ist aufschlussreich. Das war sehr hilfreich. –

+0

Die andere Motivation ist das Schreiben von DSLs, die sich gut in die Sprache integrieren, etwas wie 'using (x) {...}'. Dies macht zwar freilich auch die Typ-Inferenz für mehrere Blöcke nutzbar. –

9

Es gibt mindestens vier Möglichkeiten, um das Gleiche zu erreichen:

def myAddA(x: Int, y: Int) = x + y 
val plusFiveA: Int => Int = myAddA(5,_) 

def myAddB(x: Int)(y : Int) = x + y 
val plusFiveB = myAddB(5) _ 

def myAddC(x: Int) = (y: Int) => x + y 
val plusFiveC = myAddC(5) 

def myAddD(x: Int) = { 
    def innerD(y: Int) = x + y 
    innerD _ 
} 
val plusFiveD = myAddD(5) 

Vielleicht möchten wissen, welche oder am effizientesten ist, die die beste Art ist (für einige nicht-leistungsbezogene Maßnahme von besten).

Soweit es die Effizienz geht, stellt sich heraus, dass alle vier im Wesentlichen gleichwertig sind. Die ersten beiden Fälle emittieren tatsächlich genau denselben Bytecode; Die JVM weiß nichts über mehrere Parameterlisten. Sobald der Compiler es herausgefunden hat (Sie müssen es mit einer Typannotation für Fall A unterstützen), ist es unter der Haube gleich. Der dritte Fall ist auch extrem nah, aber da er im voraus verspricht, eine Funktion zurückzugeben und sie an Ort und Stelle zu spezifizieren, kann er ein internes Feld vermeiden. Der vierte Fall ist in Bezug auf die geleistete Arbeit ziemlich genau so wie die ersten beiden; es macht nur die Umwandlung in Function1 innerhalb der Methode statt außerhalb.

In Bezug auf Stil, schlage ich vor, dass B und C die besten Möglichkeiten sind, zu gehen, je nachdem, was Sie tun. Wenn Ihr primärer Anwendungsfall darin besteht, eine Funktion zu erstellen, die nicht mit beiden Parameterlisten aufgerufen werden soll, dann verwenden Sie C, weil es Ihnen sagt, was es tun wird. (Diese Version ist zum Beispiel auch Leuten bekannt, die aus Haskell kommen.) Wenn Sie es dagegen meistens anrufen, aber nur gelegentlich curren, dann benutzen Sie B. Noch einmal, es sagt deutlicher was Das wird erwartet.

+0

Danke, Rex. Ich kannte die Ansätze A und C nicht. Ihre Antwort ist sehr hilfreich. –

4

Ein weiterer Vorteil einer Methode, die eine Funktion direkt zurückgibt (anstelle einer partiellen Anwendung), besteht darin, dass bei der Verwendung der Infixnotation viel saubererer Code verwendet wird. Dadurch können Sie bei komplexeren Ausdrücken eine Schar von Klammern und Unterstrichen vermeiden.

Bedenken Sie:

val list = List(1,2,3,4) 

def add1(a: Int)(b: Int) = a + b 
list map { add1(5) _ }  

//versus 

def add2(a: Int) = a + (_: Int) 
list map add2(5) 
Verwandte Themen