2013-08-20 17 views
5

Ho un codice:Perché Seq.take in F # tiro System.OutOfMemoryException

seq {for i in [1 .. 100000000] -> i} |> Seq.take 100000;; 
Real: 00:00:00.000, CPU: 00:00:00.000, GC gen0: 0, gen1: 0, gen2: 0 
val it : seq<int> = 
    Error: Exception of type 'System.OutOfMemoryException' was thrown. 

Questo codice si traduce in di memoria. Perché? E perché l'eccezione è stata calcolata dopo il tempo (dopo che l'operazione è stata completata)? AFAIK, i singoli elementi della sequenza sono calcolati solo come richiesto?

+3

si forza la creazione di una lista prima di eseguire la sequenza. l'eliminazione delle parentesi quadre dovrebbe aiutare –

risposta

8
seq {for i in [1 .. 100000000] -> i} |> Seq.take 100000 

crea una sequenza che genera gli elementi come si chiede per loro, dalla lista [1 .. 100000000]. Ciò richiede che l'elenco sia in memoria. L'elenco è troppo grande per la memoria a 32 bit, quindi OutOfMemoryException. Si dovrebbe cercare

seq {for i in 1 .. 100000000 -> i} |> Seq.take 100000 
+0

Sì, mi dimentico completamente dell'elenco nell'espressione seq. – Sergey

+0

È alquanto interessante il fatto che il codice crea avidamente la lista prima di cercare pigramente di ottenere i primi 100.000 elementi. Immagino che il compilatore dovrebbe essere eccezionalmente intelligente per saltare la creazione desiderosa della lista. –

+0

@OnorioCatenacci È a causa di 'fsi', che inizia a valutare il seq (e anche il primo elemento del seq richiede l'intera lista). Vedi la risposta di @ GeneBelitski. –

6

Oltre a rivelare la ragione principale problema può valere la pena affrontare la seconda parte della domanda sul motivo per cui l'eccezione viene lanciata dopo l'operazione è stata completata.

Per arrivare a capire questo sarebbe d'aiuto se si considera che

let mySeq = seq {for i in [1 .. 100000000] -> i} |> Seq.take 100000;; 

non sarà seguita da eventuali eccezioni, mentre apparentemente innocuo

seq {for i in [1 .. 100000000] -> i};; 

sarà seguita dalla stessa eccezione come in originale, anche se apparentemente non proviamo a materializzare la sequenza comunque.

Proprio così, non lo facciamo, ma FSI fa, cercando di stampare i valori dei pochi membri prime sequenze, come per una lista più piccola in basso:

seq {for i in [1 .. 100] -> i};; 
val it : seq<int> = seq [1; 2; 3; 4; ...] 

che avvia l'istanza in memoria di la lista originale ingombrante.

Problemi correlati