2010-02-18 24 views
8

Sto provando un piccolo esperimento in haskell, chiedendomi se è possibile sfruttare la pigrizia per elaborare l'IO. Mi piacerebbe scrivere una funzione che prende una stringa (una lista di caratteri) e produce una stringa, pigramente. Vorrei quindi essere capace di nutrire pigramente i caratteri dall'IO, quindi ogni carattere sarebbe elaborato non appena fosse disponibile, e l'output sarebbe stato prodotto man mano che i caratteri necessari diventavano disponibili. Tuttavia, non sono abbastanza sicuro se/come posso produrre una lista pigra di caratteri dall'input all'interno della monade IO."Lazy IO" in Haskell?

+0

Si prega di chiarire: si desidera una funzione che accetta una stringa e "produce" una stringa --- "produce" = "restituisce", "produce in file handle" o cosa? E poi vuoi "pigramente dar da mangiare ai personaggi di IO" --- cosa significa? – dave4420

risposta

14

Le stringhe IO regolari in Haskell sono pigre. Quindi il tuo esempio dovrebbe funzionare appena fuori dalla scatola.

Ecco un esempio, utilizzando la funzione 'interagiscono', che applica una funzione di un flusso pigro di caratteri:

interact :: (String -> String) -> IO() 

Diamo filtrare la lettera 'e' dal flusso di ingresso, pigramente (cioè run a spazio costante):

main = interact $ filter (/= 'e') 

Si potrebbe anche utilizzare getContents e putStr se lo si desidera. Sono tutti pigri.

corsa per filtrare la lettera 'e' dal dizionario:

$ ghc -O2 --make A.hs 
$ ./A +RTS -s < /usr/share/dict/words 
... 
       2 MB total memory in use (0 MB lost due to fragmentation) 
... 

così vediamo che correva in un costante impronta 2M.

+2

Puoi anche vedere questo effetto sulla riga di comando. Esegui il programma "A" di Don nella shell/Terminal/qualunque sia il tuo sistema operativo, e digita direttamente il testo. Supponendo che la linea sia bufferizzata, quando premi Invio dopo ogni riga vedrai il testo filtrato immediatamente stampato, anche se il programma sembra fare solo una "chiamata" a 'filter'. – Nefrubyr

3
unsafeInterleaveIO :: IO a -> IO a 

unsafeInterleaveIO allos IO calcolo da sospendere pigramente. Quando viene passato un valore di tipo IO a, lo IO viene eseguito solo quando è richiesto il valore di a. Questo è usato per implementare la lettura di file pigri, vedere System.IO.hGetContents.

Ad esempio, main = getContents >>= return . map Data.Char.toUpper >>= putStr è pigro; man mano che i personaggi vengono aggiunti allo stdin, otterrai caratteri sullo stdout.

(Questo è lo stesso che scrivere main = interact $ map Data.Char.toUpper, come nella risposta di professori.)

7

Il metodo più semplice di fare pigro IO coinvolge funzioni quali interact, readFile, hGetContents, e simili, come indossa dice; c'è una discussione più estesa di questi nel libro Real World Haskell che potresti trovare utile. Se la memoria mi serve, tutte queste funzioni sono eventualmente implementate usando lo unsafeInterleaveIO che menziona efemerente, quindi puoi anche creare le tue funzioni in questo modo, se lo desideri.

D'altra parte, potrebbe essere opportuno notare che unsafeInterleaveIO è esattamente quello che dice sulla latta: IO non sicuro. Usandolo - o funzioni basate su di esso - breaks purity and referential transparency. Ciò consente funzioni apparentemente pure (ovvero, che non restituiscono un'azione IO) per eseguire il mondo esterno quando vengono valutate, producono risultati diversi dagli stessi argomenti e tutte quelle altre cose spiacevoli. In pratica, i modi più sensati di usare unsafeInterleaveIO non causano problemi, e gli errori semplici di solito si traducono in bug ovvi e facilmente diagnosticati, ma hai perso alcune buone garanzie.

Ci sono alternative, naturalmente; puoi trovare librerie assortite su Hackage che forniscono restrizioni, safer lazy IO o conceptually different approaches. Tuttavia, dato che i problemi si presentano solo raramente nell'uso pratico, penso che la maggior parte delle persone sia incline ad attenersi alle funzioni integrate e tecnicamente insicure.