Was ist der Unterschied zwischen den folgenden Möglichkeiten, eine Funktion in Scala zu schreiben?

8

Ich bin neu bei Scala und habe viele Möglichkeiten gesehen, um eine Funktion zu definieren, konnte aber keine klare Erklärung für die Unterschiede finden und wann ich welches Formular verwenden sollte.

Was sind die Hauptunterschiede zwischen den folgenden Funktionsdefinitionen?

  1. Mit '='

    %Vor%
  2. Ohne '='

    %Vor%
  3. Mit '= & gt;'

    %Vor%
  4. Als eine Var

    %Vor%
  5. Ohne einen Block

    %Vor%

Sie scheinen alle zu kompilieren und das gleiche Ergebnis zu liefern, wenn sie als Callback für

verwendet werden %Vor%
  • Gibt es einen Unterschied im Bytecode, den jeder generiert?
  • Gibt es irgendwelche Richtlinien, wann welches Formular verwendet werden soll?
Eran Medan 21.06.2012, 14:51
quelle

4 Antworten

19

Jede dieser Fragen wurde an anderer Stelle auf dieser Seite beantwortet, aber ich glaube nicht, dass irgendetwas sie alle zusammen behandelt. Also:

Klammern und gleich

Methoden, die mit einem Gleichheitszeichen definiert sind, geben einen Wert zurück (was auch immer die letzte Sache auswertet). Methoden, die nur mit geschweiften Klammern definiert sind, geben Unit zurück. Wenn Sie ein Gleichheitszeichen verwenden, aber das Letzte, was zu Unit ausgewertet wird, gibt es keinen Unterschied. Wenn es sich um eine einzelne Anweisung nach einem Gleichheitszeichen handelt, sind Klammern nicht erforderlich. Dies macht keinen Unterschied zum Bytecode. Also 1., 2. und 5. sind alle im Wesentlichen identisch:

%Vor%

Funktionen vs. Methoden

Eine Funktion, oft geschrieben A => B , ist eine Unterklasse von einer der Function Klassen, z.B. %Code%. Da diese Klasse eine Function1[A,B] -Methode hat, die Scala auf magische Weise aufruft, wenn Sie nur Paren ohne einen Methodennamen verwenden, sieht das wie ein Methodenaufruf aus - und das ist es, außer es ist ein Aufruf für dieses apply -Objekt! Also wenn du schreibst

%Vor%

Dann heißt es: " Function sollte eine Instanz von f3 erstellen, die eine Function1[String,Unit] -Methode hat, die aussieht wie apply ". Wenn Sie also def apply(s: String) = println(s) angeben, ruft dies zuerst% code_de% auf, um das Funktionsobjekt zu erstellen, und ruft dann die Methode f3("Hi") auf.

Es ist ziemlich verschwenderisch, das Funktionsobjekt jedes Mal zu erstellen, wenn Sie es verwenden möchten. Daher ist es sinnvoller, das Funktionsobjekt in einer Variablen zu speichern:

%Vor%

Dies enthält eine Instanz desselben Funktionsobjekts, das die f3 (Methode) zurückgeben würde, sodass Sie sie nicht jedes Mal neu erstellen müssen.

Wann verwenden Sie was

Menschen unterscheiden sich in der Konvention von apply und def . Persönlich schreibe ich alle Methoden, die : Unit = ... ohne ein Gleichheitszeichen zurückgeben - das ist ein Hinweis für mich, dass die Methode fast sicher nutzlos ist, es sei denn, sie hat eine Nebenwirkung (mutiert eine Variable, führt IO aus usw.) . Außerdem verwende ich im Allgemeinen nur geschweifte Klammern, wenn dies erforderlich ist, entweder weil es mehrere Anweisungen gibt oder weil die einzelne Anweisung so komplex ist. Ich möchte eine visuelle Hilfe, um mir zu sagen, wo sie endet.

Methoden sollten verwendet werden, wann immer Sie wollen, nun, eine Methode. Funktionsobjekte sollten immer dann erstellt werden, wenn Sie sie an eine andere Methode übergeben möchten, um sie zu verwenden (oder sie sollten immer dann als Parameter angegeben werden, wenn Sie eine Funktion anwenden möchten). Angenommen, Sie möchten einen Wert skalieren können:

%Vor%

Sie könnten einen konstanten Multiplikator liefern. Oder du könntest etwas hinzufügen und etwas vermehren. Aber am flexibelsten würden Sie nur nach einer beliebigen Funktion von { } bis Unit fragen:

%Vor%

Vielleicht haben Sie jetzt eine Vorstellung von einer Standard -Skala. Das ist wahrscheinlich überhaupt keine Skalierung. Vielleicht möchten Sie also eine Funktion, die ein Double übernimmt und das gleiche Double zurückgibt.

%Vor%

Wir speichern die Funktion in Double , weil wir sie nicht immer wieder neu erstellen wollen. Jetzt können wir diese Funktion als Standardargument verwenden:

%Vor%

Jetzt können wir sowohl Double als auch val und x.scale aufrufen und sie werden alle funktionieren.

    
Rex Kerr 21.06.2012, 15:13
quelle
5

Ja, es gibt Unterschiede im Bytecode. Und ja, es gibt Richtlinien.

  1. Mit = : Dies deklariert eine Methode, die einen Parameter akzeptiert und den letzten Ausdruck im rechten Block zurückgibt, der hier den Typ Unit hat.

  2. Ohne = : Dies deklariert eine Methode, die keinen Rückgabewert hat, dh der Rückgabetyp ist immer Unit , unabhängig vom Typ des letzten Ausdrucks im rechten Block ist.

  3. Mit => : Dies deklariert eine Methode, die ein Funktionsobjekt vom Typ scala.xml.Node => Unit zurückgibt. Jedes Mal, wenn Sie diese Methode func3 aufrufen, erstellen Sie ein neues Funktionsobjekt auf dem Heap. Wenn Sie func3(node) schreiben, rufen Sie zuerst func3 auf, das das Funktionsobjekt zurückgibt, und rufen dann das apply(node) für dieses Funktionsobjekt auf. Dies ist langsamer als nur eine einfache Methode aufzurufen, wie in den Fällen 1. und 2.

  4. Als var : Dies deklariert eine Variable und erstellt ein Funktionsobjekt wie in 3. Das Funktionsobjekt wird jedoch nur einmal erstellt . Die Verwendung dieses Aufrufs für das Funktionsobjekt ist in den meisten Fällen langsamer als nur ein einfacher Methodenaufruf (möglicherweise nicht von JIT inline), aber zumindest erstellen Sie das Objekt nicht neu. Wenn Sie die Gefahr vermeiden möchten, dass jemand die Variable func4 neu zuweist, verwenden Sie stattdessen eine val oder eine lazy val .

  5. Dies ist syntaktischer Zucker für 1. wenn Blöcke nur einen einzigen Ausdruck enthalten.

Beachten Sie, dass wenn Sie die Formulare 1., 2. und 5. mit der Methode foreach höherer Ordnung verwenden, Scala trotzdem ein Funktionsobjekt erstellen wird, das func1 , func2 oder func5 implizit aufruft, und übergebe das an foreach (es wird kein Methoden-Handle oder SMS wie dieses verwenden, zumindest nicht in aktuellen Versionen). In diesen Fällen entspricht der generierte Code ungefähr:

%Vor%

Die Richtlinie lautet also - es sei denn, Sie verwenden jedes Mal das gleiche Funktionsobjekt, erstellen Sie einfach eine gewöhnliche Methode wie in 1., 2. oder 5. Sie wird trotzdem zu einem Funktionsobjekt gehoben, wo dies erforderlich ist. Wenn Sie feststellen, dass dadurch viele Objekte generiert werden, weil der Aufruf einer solchen Methode oft geschieht, möchten Sie möglicherweise die Mikro-Optimierung mithilfe des Formulars 4 vornehmen. Stellen Sie stattdessen sicher, dass das Funktionsobjekt für foreach nur einmal erstellt wird.

Bei der Entscheidung zwischen 1., 2. und 5. ist eine Richtlinie - wenn Sie eine einzige Aussage haben, verwenden Sie Formular 5.

Wenn der Rückgabetyp Unit lautet, verwenden Sie andernfalls das Formular def foo(): Unit = { , wenn es sich um eine öffentliche API handelt, sodass Clients, die Ihren Code betrachten, den Rückgabetyp schnell und übersichtlich sehen. Verwenden Sie das def foo() { -Formular für Methoden mit dem Rückgabetyp Unit , die privat sind, zu Ihrem eigenen Vorteil mit kürzerem Code. Aber das ist nur eine bestimmte Richtlinie in Bezug auf Stil.

Weitere Informationen finden Sie unter Ссылка

    
axel22 21.06.2012 15:03
quelle
2

Nun, 1, 2 und 5 sind überhaupt keine Funktionen , sie sind Methoden , die sich grundlegend von Funktionen unterscheiden: Methoden gehören zu Objekte und sind selbst keine Objekte, während Funktionen Objekte sind.

1, 2 und 5 sind auch genau gleich: Wenn Sie nur eine Anweisung haben, brauchen Sie keine geschweiften Klammern, um mehrere Anweisungen zu gruppieren. Ergo 5 ist also gleich 1. Verlassen Sie das = -Zeichen ist syntaktischer Zucker zum Deklarieren eines Rückgabetyps von Unit , aber Unit ist auch der abgeleitete Rückgabetyp für 1 und 5, also ist 2 gleich wie 1 und 5.

3 ist eine Methode, die beim Aufruf eine Funktion zurückgibt. 4 ist eine Variable, die auf eine Funktion zeigt.

    
Jörg W Mittag 21.06.2012 15:06
quelle
1

1-2. Wenn du Gleichheitszeichen wegwirfst, wird deine Funktion zur Prozedur (gibt Einheit oder einfach nichts zurück).
3. Im dritten Fall haben Sie eine Funktion scala.xml.Node => Unit definiert, die eine Funktion zurückgibt.
4. Gleich, aber Sie haben der Variable eine Funktion scala.xml.Node => Unit zugewiesen. Der Unterschied erklärt in Unterschiede zwischen diesen drei Möglichkeiten zur Definition einer Funktion in Scala 5. Kein Unterschied, verglichen mit 1. Aber Sie können keine mehrzeiligen Anweisungen wie diese schreiben.

    
om-nom-nom 21.06.2012 15:03
quelle

Tags und Links