webentwicklung-frage-antwort-db.com.de

Was ist der Unterschied zwischen "def" und "val", um eine Funktion zu definieren

Was ist der Unterschied zwischen:

def even: Int => Boolean = _ % 2 == 0

und 

val even: Int => Boolean = _ % 2 == 0

Beide können wie even(10) aufgerufen werden.

195
Amir Karimi

Die Methode def even wird beim Aufruf ausgewertet und erstellt jedes Mal eine neue Funktion (neue Instanz von Function1).

def even: Int => Boolean = _ % 2 == 0
even eq even
//Boolean = false

val even: Int => Boolean = _ % 2 == 0
even eq even
//Boolean = true

Mit def erhalten Sie bei jedem Anruf eine neue Funktion:

val test: () => Int = {
  val r = util.Random.nextInt
  () => r
}

test()
// Int = -1049057402
test()
// Int = -1049057402 - same result

def test: () => Int = {
  val r = util.Random.nextInt
  () => r
}

test()
// Int = -240885810
test()
// Int = -1002157461 - new result

val wertet die definierte Definition aus, def - beim Aufruf:

scala> val even: Int => Boolean = ???
scala.NotImplementedError: an implementation is missing

scala> def even: Int => Boolean = ???
even: Int => Boolean

scala> even
scala.NotImplementedError: an implementation is missing

Beachten Sie, dass es eine dritte Option gibt: lazy val.

Beim ersten Aufruf wird ausgewertet:

scala> lazy val even: Int => Boolean = ???
even: Int => Boolean = <lazy>

scala> even
scala.NotImplementedError: an implementation is missing

Gibt jedoch jedes Mal dasselbe Ergebnis zurück (in diesem Fall dieselbe Instanz von FunctionN):

lazy val even: Int => Boolean = _ % 2 == 0
even eq even
//Boolean = true

lazy val test: () => Int = {
  val r = util.Random.nextInt
  () => r
}

test()
// Int = -1068569869
test()
// Int = -1068569869 - same result

Leistung

val wertet die Definition aus.

def wird bei jedem Anruf ausgewertet, daher kann die Leistung bei mehreren Anrufen schlechter sein als val. Sie erhalten die gleiche Leistung mit einem einzigen Anruf. Und ohne Anrufe erhalten Sie von def keinen Overhead. Sie können ihn also definieren, auch wenn Sie ihn in einigen Zweigen nicht verwenden.

Mit einem lazy val erhalten Sie eine faule Bewertung: Sie können sie auch definieren, wenn Sie sie in einigen Zweigen nicht verwenden, und sie wird einmal oder nie ausgewertet. Sie erhalten jedoch ein wenig zusätzlichen Aufwand, wenn Sie bei jedem Zugriff auf Ihr System einen doppelten Haken setzen lazy val.

Wie @SargeBorsch feststellte, könnte man eine Methode definieren, und dies ist die schnellste Option:

def even(i: Int): Boolean = i % 2 == 0

Wenn Sie jedoch eine Funktion (keine Methode) für die Funktionszusammenstellung oder für Funktionen höherer Ordnung (wie filter(even)) benötigen, generiert der Compiler bei jeder Verwendung einer Funktion eine Funktion aus Ihrer Methode. Die Leistung könnte daher etwas schlechter sein als bei val.

301
senia

Bedenken Sie: 

scala> def even: (Int => Boolean) = {
             println("def"); 
             (x => x % 2 == 0)
       }
even: Int => Boolean

scala> val even2: (Int => Boolean) = {
             println("val");
             (x => x % 2 == 0)
       }
val //gets printed while declaration. line-4
even2: Int => Boolean = <function1>

scala> even(1)
def
res9: Boolean = false

scala> even2(1)
res10: Boolean = false

Siehst du den unterschied Zusamenfassend:

def : Bei jedem Aufruf von even wird erneut der Rumpf der even-Methode aufgerufen. Mit even2 d.h. val wird die Funktion jedoch nur einmal während der Deklaration initialisiert (und daher val in Zeile 4 und nie wieder ausgegeben). Bei jedem Zugriff wird dieselbe Ausgabe verwendet. Versuchen Sie dies beispielsweise:

scala> import scala.util.Random
import scala.util.Random

scala> val x = { Random.nextInt }
x: Int = -1307706866

scala> x
res0: Int = -1307706866

scala> x
res1: Int = -1307706866

Wenn x initialisiert wird, wird der von Random.nextInt zurückgegebene Wert als Endwert von x festgelegt. Bei der nächsten Verwendung von x wird immer derselbe Wert zurückgegeben.

Sie können auch x faul initialisieren. das erste Mal, wenn es verwendet wird, wird es initialisiert und nicht während der Deklaration. Zum Beispiel:

scala> lazy val y = { Random.nextInt }
y: Int = <lazy>

scala> y
res4: Int = 323930673

scala> y
res5: Int = 323930673
23
Jatin

Sieh dir das an:

  var x = 2 // using var as I need to change it to 3 later
  val sq = x*x // evaluates right now
  x = 3 // no effect! sq is already evaluated
  println(sq)

Überraschenderweise werden 4 und nicht 9 gedruckt! val (even var) wird sofort ausgewertet und zugewiesen.
Ändern Sie nun val in def .. es werden 9 gedruckt! Def ist ein Funktionsaufruf. Er wird jedes Mal ausgewertet, wenn er aufgerufen wird.

4
Apurva Singh

val, d. h. "sq" wird durch Scala-Definition festgelegt. Es wird direkt zum Zeitpunkt der Deklaration ausgewertet, Sie können es später nicht mehr ändern. In anderen Beispielen ist even2 auch val, aber es wurde mit der Funktionssignatur deklariert, d. H. "(Int => Boolean)", also nicht vom Int-Typ. Es ist eine Funktion, deren Wert durch den folgenden Ausdruck festgelegt wird

   {
         println("val");
         (x => x % 2 == 0)
   }

Gemäß der Scala-Eigenschaft können Sie even2 keine andere Funktion zuweisen, dieselbe Regel wie sq.

Warum werden eval2 val-Funktionen nicht immer und immer wieder aufgerufen?

Ursprungscode:

val even2: (Int => Boolean) = {
             println("val");
             (x => x % 2 == 0)
       }

Wir wissen, dass in Scala die letzte Aussage des obigen Ausdrucks (in {..}) tatsächlich zur linken Seite zurückkehrt. Am Ende setzen Sie even2 auf die Funktion "x => x% 2 == 0", die mit dem Typ übereinstimmt, den Sie für even2 val type definiert haben, d. H. (Int => Boolean). Der Compiler ist also glücklich. Nun zeigt even2 nur auf die Funktion "(x => x% 2 == 0)" (keine andere Anweisung vor dh println ("val") usw. Beim Aufruf von event2 mit anderen Parametern werden tatsächlich "(x => x% 2) aufgerufen == 0) "Code, da nur dieser mit event2 gespeichert wird.

scala> even2(2)
res7: Boolean = true

scala> even2(3)
res8: Boolean = false

Nur um dies zu verdeutlichen, folgt eine andere Version des Codes.

scala> val even2: (Int => Boolean) = {
     |              println("val");
     |              (x => { 
     |               println("inside final fn")
     |               x % 2 == 0
     |             })
     |        }

Was wird passieren ? hier sehen wir "inside final fn" immer wieder gedruckt, wenn Sie even2 () aufrufen.

scala> even2(3)
inside final fn
res9: Boolean = false

scala> even2(2)
inside final fn
res10: Boolean = true

scala> 
1
Sandi

Wenn Sie eine Definition wie def x = e ausführen, wird der Ausdruck e nicht ausgewertet. Stattdessen wird e ausgewertet, wenn x aufgerufen wird. 

Alternativ bietet Scala eine Wertdefinition val x = e an, die die rechte Seite als Teil der Auswertung der Definition auswertet. Wenn dann x verwendet wird, wird es sofort durch den vorberechneten Wert von e ersetzt, sodass der Ausdruck nicht erneut ausgewertet werden muss.

1
Gaurav Khare

Neben den obigen hilfreichen Antworten lauten meine Erkenntnisse:

def test1: Int => Int = {
x => x
}
--test1: test1[] => Int => Int

def test2(): Int => Int = {
x => x+1
}
--test2: test2[]() => Int => Int

def test3(): Int = 4
--test3: test3[]() => Int

Das obige zeigt, dass "def" eine Methode ist (mit Parametern ohne Parameter), die beim Aufruf eine andere Funktion "Int => Int" zurückgibt. 

Die Konvertierung von Methoden in Funktionen wird hier gut erklärt: https://tpolecat.github.io/2014/06/09/methods-functions.html

0
prateek

In REPL

scala> def even: Int => Boolean = { _% 2 == 0 }
even: Int => Boolean

scala> val even: Int => Boolean = { _% 2 == 0 }
even: Int => Boolean = $$Lambda$1157/[email protected]

def bedeutet call-by-name, wird bei Bedarf ausgewertet

val bedeutet call-by-value, wird während der Initialisierung ausgewertet

0
GraceMeng

außerdem ist Val eine Wertbewertung. Was bedeutet, dass der Ausdruck auf der rechten Seite während der Definition ausgewertet wird. Wobei Def nach Namen ausgewertet wird. Es wird erst ausgewertet, wenn es verwendet wird.

0
Sandipan Ghosh