lazy Funktionsdefinitionen in scala
-
01-10-2019 - |
Frage
Ich habe scala lernen, und ich muss sagen, dass es eine wirklich coole Sprache ist. Besonders gut gefällt mir seine Pattern-Matching-Funktionen und Funktionsliteralen aber ich komme aus einer Javascript, Rubin Hintergrund und einer meiner Lieblings-Muster in diesen Sprachen ist die faul Funktion und Methodendefinition Muster. Ein Beispiel in Javascript ist
var foo = function() {
var t = new Date();
foo = function() {
return t;
};
return foo();
};
Der gleiche Code mit kleineren Verbesserungen arbeitet in Rubin, wo man nur das Singleton-Objekt verwenden Sie die Methode neu zu definieren, nachdem die Berechnung durchgeführt wird. Diese Art der Sache kommt in wirklich praktisch, wenn teure Berechnung beteiligt ist, und Sie müssen nicht im Voraus wissen, wenn Sie das Ergebnis gehen zu müssen. Ich weiß, dass in scala ich einen Cache verwenden kann, die gleiche Art von Ergebnis zu simulieren, aber ich versuche bedingte Kontrollen zu vermeiden und so weit meine Experimente haben negative Ergebnisse zurückgegeben. Hat jemand wissen, ob es ein fauler Funktion oder Methode Definitionsmuster in scala ist?
Hinweis: Der Javascript-Code ist von Peter Michaux Website <. / p>
Lösung
Alles, was Code in JavaScript kompliziert erscheint nur versuchen, den Wert des Datums cachen. In Scala, können Sie das gleiche erreichen trivialer:
lazy val foo = new Date
Und wenn Sie wollen nicht einmal eine val machen, sondern wollen eine Funktion aufzurufen, die nur den teuren Code ausführen, wenn sie sie braucht, können Sie
def maybeExpensive(doIt: Boolean, expensive: => String) {
if (doIt) println(expensive)
}
maybeExpensive(false, (0 to 1000000).toString) // (0 to 1000000).toString is never called!
maybeExpensive(true, (0 to 10).toString) // It is called and used this time
, wo das Muster expensive: => String
einen Neben Namen Parameter aufgerufen wird, die man sich vorstellen kann, wie: „Gib mir etwas, das einen String auf Anfrage generieren.“ Beachten Sie, dass, wenn Sie es zweimal verwenden, wird es jedes Mal regenerieren, das ist, wo Randall Schultz‘handliche Muster kommt in:
def maybeExpensiveTwice(doIt: Boolean, expensive: => String) {
lazy val e = expensive
if (doIt) {
println(e)
println("Wow, that was " + e.length + " characters long!")
}
}
Jetzt erzeugen Sie nur, wenn Sie es brauchen (über die by-name-Parameter) und lagert und wiederverwenden es, wenn Sie es wieder benötigen (über die faule val).
es also tun, um diese Art und Weise, nicht die JavaScript-Art und Weise, auch wenn Sie könnte machen Scala viel wie der JavaScript aussehen.
Andere Tipps
Scala hat lazy val
s, deren initializers werden nicht ausgewertet, sofern und solange das val verwendet wird. Lazy vals als Methode lokale Variablen verwendet werden können.
Scala hat auch nach Namen Parameter Methode, deren tatsächlichen Parameter Ausdrücke werden in einem Thunk gewickelt und dass thunk jedes Mal ausgewertet wird der formale Parameter in der Methode Körper verwiesen wird.
Zusammen stellen diese verwendet werden können lazy evaluation Semantik wie sind die Standards in Haskell (zumindest in meinem sehr begrenzten Verständnis von Haskell) zu erreichen.
def meth(i: => Int): Something = {
// ^^^^^^ by-name parameter syntax
lazy val ii = i
// Rest of method uses ii, not i
}
Bei diesem Verfahren wird der Ausdruck als eigentlicher Parameter verwendet werden ausgewertet entweder Null mal (wenn der dynamische Ausführungspfad des Verfahrens Körper nie ii
verwendet) oder einmal (wenn es ii
ein oder mehrere Male verwendet).
Sie können eine faule val definieren, die eine Funktion ist:
lazy val foo = {
val d = new Date
() => { d }
}
println(foo())
foo()
wird nun die selber als Date-Objekt jedes Mal, Objekt, das das erste Mal foo aufgerufen werden initialisiert wird.
den Code ein wenig zu erklären, das erste Mal, foo () aufgerufen { val d = new Date; () => { d } }
ausgeführt wird, d auf ein neues Datum Wert zugewiesen wird dann bewerten sie den letzten Ausdruck () => { d }
und weisen Sie auf die foo Wert. Dann foo ist eine Funktion ohne Parameter, die Rückkehr d.
Ich denke, einige der Responder ein wenig durch die Art und Weise Sie die Frage formuliert verwirrt waren. Das Scala-Konstrukt Sie hier wollen, ist eine einfache faul Definition:
lazy val foo = new java.util.Date
Der Aufbau des Date-Objekt höchstens einmal auftreten und bis zum ersten Verweis auf foo verschoben werden.
ich nichts gewusst zu Ruby, aber scala hat Singleton-Objekt Muster auch:
Welcome to Scala version 2.8.0.r22634-b20100728020027 (Java HotSpot(TM) Client VM, Java 1.6.0_20).
Type in expressions to have them evaluated.
Type :help for more information.
scala> object LazyInit {
| val msec = { println("Hi,I'm here!"); System.currentTimeMillis }
| }
defined module LazyInit
scala> System.currentTimeMillis
res0: Long = 1282728315918
scala> println(System.currentTimeMillis +" : " + LazyInit.msec)
Hi,I'm here!
1282728319929 : 1282728319930
scala> println(System.currentTimeMillis +" : " + LazyInit.msec)
1282728322936 : 1282728319930
scala> println(System.currentTimeMillis +" : " + LazyInit.msec)
1282728324490 : 1282728319930
scala>
Wenn Sie Funktion erhalten möchten, können Sie es von einem Funktionstyp machen Subtyp:
scala> object LazyFun extends (() => Long) {
| val msec = System.currentTimeMillis
| def apply() = msec
| }
defined module LazyFun
scala> System.currentTimeMillis
res2: Long = 1282729169918
scala> println(System.currentTimeMillis + " : " + LazyFun())
1282729190384 : 1282729190384
scala> println(System.currentTimeMillis + " : " + LazyFun())
1282729192972 : 1282729190384
scala> println(System.currentTimeMillis + " : " + LazyFun())
1282729195346 : 1282729190384
Ich denke, was Sie meinen „lazy-Funktion“ ist Funktionsliteral oder anonyme Funktion.
In Scala Sie Dinge wie dies tun könnte, sehr ähnlich wie die Javascript-Code Sie auf dem Laufenden.
val foo = () => {
val t = new Date()
val foo = () => {t}
foo()
}
println ("Hello World:" + foo())
Der Hauptunterschied besteht darin, dass:
- Sie konnte nicht Neuzuordnung der äußere foo
- Es gibt keine "Funktion" Keyword, stattdessen verwenden Sie so etwas wie (s: String) => {code}
- Die letzte Aussage ist der Rückgabewert eines Blocks, so dass Sie nicht brauchen, „return“ hinzufügen.