2012-03-21 11 views
35

Nel file Parsers.scala (Scala 2.9.1) dalla libreria del parser combinators mi sembra di aver trovato una funzione Scala meno conosciuta chiamata "lazy arguments". Ecco un esempio:Argomenti pigri di Scala: come funzionano?

def ~ [U](q: => Parser[U]): Parser[~[T, U]] = { lazy val p = q // lazy argument 
    (for(a <- this; b <- p) yield new ~(a,b)).named("~") 
} 

A quanto pare, c'è qualcosa da fare qui con l'assegnazione dell'argomento q chiamata per nome alla Val pigro p.

Finora non sono stato in grado di capire cosa fa e perché è utile. Qualcuno può aiutare?

+1

Avete mettere alcuno sforzo nel tentativo di scoprire te stesso? Fa parte del linguaggio Scala e una ricerca su Internet dovrebbe rivelare abbastanza successi su scala scala. – ziggystar

+1

@ziggystar: ho già effettuato 2-3 ricerche su Google e non ho trovato nulla di utile. Argomenti pigri sono stati menzionati su alcune richieste della funzione Scala, ma non è stata fornita alcuna spiegazione di ciò che ho potuto dare un senso. –

+1

@ziggystar: la richiesta della funzione è qui: https://issues.scala-lang.org/browse/SI-240. Inoltre, la ricerca di 'scala lazy' o anche' scala lazy argument' non sembra fornire molte informazioni utili perché in genere si ottengono risultati relativi a cose più semplici come lazy val's e call-by-name. –

risposta

76

Gli argomenti denominati per nome sono chiamati ogni volta che vengono richiesti. I valori Lazy sono chiamati la prima volta e quindi il valore viene memorizzato. Se lo chiedi di nuovo, otterrai il valore memorizzato.

Così, un modello come

def foo(x: => Expensive) = { 
    lazy val cache = x 
    /* do lots of stuff with cache */ 
} 

è l'ultimo put-off-work-as-long-as-possibile-and-only-fai-da-una volta pattern. Se il tuo percorso di codice non ti porta mai ad avere bisogno di x, allora non verrà mai valutato. Se ne hai bisogno più volte, verrà valutato solo una volta e conservato per uso futuro. Così fai la chiamata costosa o zero (se possibile) o uno (se non) volte, garantito.

+1

+1, avevo pensato che chiamare per nome fosse in qualche modo pigro, ma non per niente! Grazie per il chiarimento ... – virtualeyes

+0

alias call by need – JPC

20

L'articolo di Wikipedia per Scala risponde anche quello che la parola chiave lazy fa:

Utilizzando la parola chiave pigro rinvia l'inizializzazione di un valore fino a quando si utilizza questo valore.

Inoltre, ciò che si ha in questo esempio di codice con q : => Parser[U] è un parametro Call-by-name. Un parametro dichiarato in questo modo rimane non valutato, fino a quando non lo valuti esplicitamente da qualche parte nel tuo metodo.

Ecco un esempio dal REPL scala su come i parametri Call-by-Work Nome:

scala> def f(p: => Int, eval : Boolean) = if (eval) println(p) 
f: (p: => Int, eval: Boolean)Unit 

scala> f(3, true) 
3 

scala> f(3/0, false) 

scala> f(3/0, true) 
java.lang.ArithmeticException:/by zero 
    at $anonfun$1.apply$mcI$sp(<console>:9) 
    ... 

Come si può vedere, la 3/0 non ottiene affatto valutato in seconda convocazione. La combinazione del valore lazy con un parametro call-by-name come sopra comporta il seguente significato: il parametro q non viene valutato immediatamente quando si chiama il metodo. Viene invece assegnato al valore lazy p, che non viene valutato immediatamente. Solo lateralmente, quando viene utilizzato lo p, questo porta alla valutazione di q. Tuttavia, poiché p è un val, il parametro q verrà valutato solo una volta e il risultato verrà archiviato in p per il successivo riutilizzo nel ciclo.

Si può facilmente vedere nella repl, che la valutazione multipla può accadere altrimenti:

scala> def g(p: => Int) = println(p + p) 
g: (p: => Int)Unit 

scala> def calc = { println("evaluating") ; 10 } 
calc: Int 

scala> g(calc) 
evaluating 
evaluating 
20 
Problemi correlati