2013-07-17 10 views
6

Per esempio, se abbiamo un metodo comeCome implementa Scala il ritorno dall'interno di un'espressione?

def find[A](xs: Seq[A], p: A => Boolean): Option[A] = { 
    xs.foreach(x => if (p(x)) return Some(x)); 
    None; 
} 

(ovviamente v'è una funzione di libreria per questo, questo è solo un esempio). Come si evita l'esecuzione foreach quando la funzione interna è return s?

O in

def foo(x: AnyRef): String = 
    process(x match { 
    case (s: String) => s; 
    case _   => return ""; 
    }) 

come si fa l'evitare di esecuzione in esecuzione process quando return "" viene rilasciato?

+5

[Scala usa eccezioni per il controllo di flusso come questo] (http://dev.bizo.com/2010/01/scala-supports-non-local-returns.html) (parola chiave per cercare è 'NonLocalReturnControl '). Vedi anche * Specifiche linguistiche Scala, sezione 6.20 * –

+1

Vedi anche http://stackoverflow.com/a/9707524/247533 –

risposta

3

L'foo esempio dipende da quale delle

def process(s: String): String 
def process(s: => String): String 

è. Sto assumendo il primo, dal momento che suggerisci di non eseguire process. Questo è il modo in cui funziona sempre quando si passa un argomento: prima si crea il lavoro di creazione dell'argomento, quindi si chiama il metodo. Dal momento che ti imbatti in uno return, è facile: basta chiamare lo return appropriato dal codice byte durante la creazione dell'argomento *, e non proseguire mai per invocare il metodo. Quindi è solo un ritorno locale.

L'esempio find è un po 'più coinvolto. Proviamo un massimo semplice esempio motivato da un foo che richiede un ritorno non locale:

class Nonlocal { 
    def pr(s: => String) = { println(s); "Printed" } 

    def foo(x: AnyRef): String = pr(x match { 
    case (s: String) => s; 
    case _   => return ""; 
    }) 
} 

Il corpo di foo è equivalente a

import scala.runtime.NonLocalReturnControl 
val temp = new AnyRef 
try { 
    pr(x match { 
    case s: String => s 
    case _   => throw new NonLocalReturnControl(temp, "") 
    }) 
} 
catch { 
    case nlrc: NonLocalReturnControl[_] if (nlrc.key eq temp) => 
    nlrc.value.asInstanceOf[String] 
} 

Le cose principali da notare è che un oggetto sentinella viene creato in modo che queste cose possono essere annidate arbitrariamente senza scombussolarsi, e che NonLocalReturnControl riporta il valore corretto. Non sorprende che questo non sia esattamente economico rispetto al semplice ritorno, ad esempio, di uno Int. Ma dal momento che crea un'eccezione senza una traccia dello stack (sicuro, perché non può sfuggire: il blocco catch è garantito per catturarlo), non è che non valido - è tanto grave quanto chiamare una funzione trigonometrica o sommare un array con poche decine di voci.

Si noti inoltre che pr ottiene solo parzialmente eseguito prima che l'eccezione lo ottenga. In questo caso, non stampa nulla perché la prima cosa che fa è provare a usare s per riempire una stringa vera, ma poi colpisce l'eccezione che riporta il controllo a foo. (Quindi si ottiene una stringa vuota da foo, ma non si stampa nulla.)

* In realtà, in bytecode tende ad essere un salto alla fine del metodo, con il carico/ritorno lì. Concettualmente irrilevante, però.