Differenze tra questi tre modi di definire una funzione in Scala


92

Dati tre modi per esprimere la stessa funzione f(a) := a + 1:

val f1 = (a:Int) => a + 1
def f2 = (a:Int) => a + 1
def f3:(Int => Int) = a => a + 1

In cosa differiscono queste definizioni? Il REPL non indica differenze evidenti:

scala> f1
res38: (Int) => Int = <function1>
scala> f2
res39: (Int) => Int = <function1>
scala> f3
res40: (Int) => Int = <function1>

11
Si noti che nel 2 ° blocco sopra, la valutazione f1in REPL mostra il valore staticamente vincolato a f1durante la valutazione f2e f3mostra il risultato dell'invocazione di quei metodi. In particolare, una nuova Function1[Int, Int]istanza viene prodotta ogni volta f2o f3viene invocata, mentre f1è la stessa Function1[Int, Int]per sempre.
Randall Schulz

@RandallSchulz dato che la versione val non richiede una nuova istanza di funzione, perché mai si dovrebbe usare def in questo caso?
virtualeyes

2
@virtualeyes L'unica situazione che posso ricordare in cui si vede defs che fornisce valori FunctionN [...] è nella libreria del parser del combinatore. Non è molto comune scrivere metodi che producono funzioni e virtualmente non si userebbe mai un def per produrre molte copie di una funzione semanticamente / funzionalmente immutabile.
Randall Schulz

Risposte:


112

f1 è una funzione che accetta un numero intero e restituisce un numero intero.

f2è un metodo con zero arità che restituisce una funzione che accetta un numero intero e restituisce un numero intero. (Quando si digita f2in REPL in seguito, diventa una chiamata al metodo f2.)

f3è uguale a f2. Semplicemente non stai impiegando l'inferenza del tipo lì.


6
Perché f1un functioned f2è un method?
Freewind

17
@Freewind, una funzione è un oggetto con un metodo denominato apply. Un metodo, beh, è ​​un metodo.
missingfaktor

Risposta fantastica. Domanda: dici che f2 ha arità zero, ma non è unario? en.wikipedia.org/wiki/Arity "Una funzione nulla non accetta argomenti. Una funzione unaria accetta un argomento." Solo curioso!
Matthew Cornell

5
@ MatthewCornell, di per f2sé non accetta argomenti. L'oggetto funzione che restituisce lo fa.
Missingfaktor

122

All'interno di una classe, valviene valutato all'inizializzazione mentre defviene valutato solo quando, e ogni volta , viene chiamata la funzione. Nel codice seguente vedrai che x viene valutato la prima volta che l'oggetto viene utilizzato, ma non di nuovo quando si accede al membro x. Al contrario, y non viene valutato quando viene creata un'istanza dell'oggetto, ma viene valutato ogni volta che si accede al membro.

  class A(a: Int) {
    val x = { println("x is set to something"); a }
    def y = { println("y is set to something"); a }
  }

  // Prints: x is set to something
  val a = new A(1)

  // Prints: "1"
  println(a.x)

  // Prints: "1"                               
  println(a.x)

  // Prints: "y is set to something" and "1"                                  
  println(a.y)

  // Prints: "y is set to something" and "1"                                                                                   
  println(a.y)

@JacobusR è vero solo all'interno di una classe?
Andrew Cassidy

ad esempio: scala> var b = 5 b: Int = 5 scala> val a: (Int => Int) = x => x + ba: Int => Int = <function1> scala> a (5) res48: Int = 10 scala> b = 6 b: Int = 6 scala> a (5) res49: Int = 11 Mi aspettavo che a (5) restituisse 10 e il valore di b fosse stato inline
Andrew Cassidy

@AndrewCassidy la funzione aè immutabile e valutata all'inizializzazione, ma brimane un valore modificabile. Quindi il riferimento a bviene impostato durante l'inizializzazione, ma il valore memorizzato da brimane mutabile. Per divertimento ora potresti crearne uno nuovo val b = 123. Dopodiché a(5)darai sempre 11, poiché bora è un valore completamente nuovo.
Jack

@JacobusR grazie ... questo ha senso. Ciò coincide con la definizione di "ambito lessicale" poiché la funzione a porta un riferimento alla "var b" originale. Immagino che quello che mi ha fatto confondere è che dire: var b = 5; val c = b; b = 6; agisce diversamente. Immagino che non dovrei aspettarmi che una definizione di funzione che trasporta riferimenti all'ambito "lessicale" originale si comporti allo stesso modo di un Int.
Andrew Cassidy

3

L'esecuzione di una definizione come def x = e non valuterà l'espressione e . Invece e viene valutato ogni volta che viene utilizzato x . In alternativa, Scala offre una definizione di valore val x = e , che valuta il lato destro e come parte della valutazione della definizione. Se x viene quindi utilizzato successivamente, viene immediatamente sostituito dal valore precalcolato di e , in modo che l'espressione non debba essere valutata di nuovo.

Scala By Example di Martin Odersky

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.