Lo chiederò con un esempio di Scala, ma potrebbe essere che ciò influenzi altri linguaggi che consentono stili imperativi e funzionali ibridi.Come evitare di catturare involontariamente l'ambito locale nei valori letterali delle funzioni?
Ecco un breve esempio (AGGIORNATO, vedi sotto):
def method: Iterator[Int] {
// construct some large intermediate value
val huge = (1 to 1000000).toList
val small = List.fill(5)(scala.util.Random.nextInt)
// accidentally use huge in a literal
small.iterator filterNot (huge contains _)
}
Ora iterator.filterNot
opere pigramente, che è grande! Di conseguenza, ci aspetteremmo che l'iteratore restituito non consumerà molta memoria (infatti, O (1)). Purtroppo, tuttavia, abbiamo commesso un terribile errore: poiché filterNot
è pigro, mantiene un riferimento alla funzione letterale huge contains _
.
Così mentre pensavamo che il metodo richiedesse una grande quantità di memoria mentre era in esecuzione, e che quella memoria potesse essere liberata immediatamente dopo la fine del metodo, infatti quella memoria è bloccata fino a quando non dimentichiamo il reso Iterator
.
(Ho appena fatto un tale errore, che ha avuto molto tempo per rintracciare! Si può prendere queste cose guardando mucchio discariche ...)
Quali sono le migliori pratiche per evitare questo problema?
Sembra che l'unica soluzione sia controllare attentamente i valori letterali delle funzioni che sopravvivono alla fine dell'ambito e quali hanno catturato le variabili intermedie. Questo è un po 'imbarazzante se stai costruendo una raccolta non rigida e pianifichi di restituirla. Qualcuno può pensare a qualche bel trucco, specifico per Scala o altro, che eviti questo problema e mi permetta di scrivere un bel codice?
AGGIORNAMENTO: l'esempio che ho fornito in precedenza era stupido, come dimostra la risposta di huynhjl qui sotto. Era stato:
def method: Iterator[Int] {
val huge = (1 to 1000000).toList // construct some large intermediate value
val n = huge.last // do some calculation based on it
(1 to n).iterator map (_ + 1) // return some small value
}
Infatti, ora che ho capito un po 'meglio come funzionano queste cose, io non sono così preoccupato!
Huh, davvero! Cercherò di trovare un esempio migliore domani, ma per ora sono confuso * perché * stai vedendo quello che hai fatto. Qualcuno può dare un buon riassunto di quali variabili locali sono catturate da quella funzione letterale? –