Haskell: inserimento di ogni riga di un file in un elenco
Domanda
Attualmente sto lavorando al progetto con Haskell e ho riscontrato dei problemi. Dovrei leggere e inserire in una lista ogni riga in un & Quot; dizionario.txt & Quot; file, ma non riesco a farlo. Ho questo codice:
main = do
let list = []
loadNums "dictionary.txt" list
loadNums location list = do
inh <- openFile location ReadMode
mainloop inh list
hClose inh
mainloop inh list = do
ineof <- hIsEOF inh
if ineof
then return ()
else do
inpStr <- hGetLine inh
inpStr:list
mainloop inh list
Dovrebbe ottenere ogni riga (so che ottiene ogni riga, poiché sostituisce " inpStr: list " con un " putStrLn inpStr " funziona correttamente, visualizzando tutte le righe) e inseriscilo in un elenco ma viene visualizzato il seguente errore:
Couldn't match expected type `IO' against inferred type `[]'
Probabilmente perché hGetLine non è una stringa, ma una stringa IO, che non ho idea di come gestire per ottenere una stringa corretta che posso inserire nel mio elenco. Non ho idea di come possa essere risolto, o quale sia esattamente il problema, ma se qualcuno ha idea di come ottenere correttamente ogni riga di un file in un elenco, lo apprezzerei.
Grazie in anticipo!
Soluzione
Nella riga in cui si verifica l'errore, Haskell si aspetta " IO a " ;, ma gli stai dando un []. Semplificando molto le cose, su un blocco do sulla monade IO, ogni riga è:
- Qualcosa che restituisce un valore di " IO a " genere; il valore di " a " il tipo al suo interno viene scartato (quindi il " a " è spesso " () ")
- A < - espressione, che fa la stessa cosa ma invece di scartare il valore di " a " type gli dà il nome a sinistra di < -
- Un let, che non fa altro che dare un nome a un valore
In questo blocco, il " hGetLine inh " restituisce un " IO String " ;, e la stringa al suo interno viene estratta e data il nome inpStr. La riga successiva, poiché non è né let né & Lt; -, dovrebbe avere un tipo & Quot; IO a & Quot ;, che non ha (causando così l'errore del compilatore). Quello che puoi fare invece, dato che hai già la String, è let:
let list' = inpStr:list
Questo crea un nuovo elenco costituito dalla stringa seguita dall'elenco originale e gli dà il nome di " list '" ;.
Cambia la seguente riga per usare " list '" invece di " list " (passando così il nuovo elenco). Quella linea chiama (ricorsivamente) mainloop, che leggerà un'altra linea, chiamerà se stessa e così via. Dopo aver letto l'intero file, restituirà qualcosa con & Quot; IO () & Quot; genere. Questo & Quot; IO () & Quot; verrà restituito al blocco do su loadNums. Congratulazioni, hai appena creato un elenco con le righe lette dal file, in ordine inverso (dal momento che stavi accodando in testa all'elenco), e quindi non hai fatto nulla.
Se vuoi farci qualcosa, cambia " return () " a " lista di ritorno " ;; il ritorno genererà un valore di tipo " IO [String] " ;, con l'elenco al suo interno (return non fa altro che incapsulare il valore), che puoi estrarre su loadNums con < - sintassi.
Il resto è lasciato come esercizio al lettore.
Altri suggerimenti
A meno che questo non sia per i compiti o cose del genere, non c'è motivo di fare così tanti sforzi. Riutilizzare è pigro!
getLines = liftM lines . readFile
main = do
list <- getLines "dictionary.txt"
mapM_ putStrLn list
Ma mentre sembra che tu stia ancora imparando Haskell, è importante per te capire cosa ha scritto CesarB.