2009-05-06 12 views
8

Ho una lista di tuple di formato [(String, String)] e ho bisogno di una funzione per scrivere il contenuto della lista in un file di testo, quindi un'altra funzione leggere questo file di testo come lo stesso elenco di tuple. Ecco cosa ho per la funzione di salvataggio:Haskell: Scrittura di file di testo e analisi in formato originale

save :: Table -> IO() 
save [] = writeFile "database.txt" "" 
save zs = do { writeFile "database.txt" "" ; sequence_ [appendFile "database.txt" ("("++a++","++b++")\n") | (a,b) <- zs] } 

Sarebbe un buon formato per il file di testo? Allora come potrei essere in grado di leggere quel file di testo e convertirlo nuovamente nella lista delle tuple?

risposta

12

Definita in Prelude,

type ShowS = String -> String 
class Show a where 
    showsPrec :: Int -> a -> ShowS 
    show :: a -> String 
    showList :: [a] -> ShowS 

type ReadS a = String -> [(a, String)] 
class Read a where 
    readsPrec :: Int -> ReadS a 
    readList :: ReadS [a] 
read :: (Read a) => String -> a 

In breve, questi sono i metodi standard "serializzazione" in Haskell. show :: (Show a) => a -> String può trasformare qualsiasi cosa che sia un'istanza di Show in una stringa e read :: (Read a) => String -> a può trasformare una stringa in qualsiasi cosa che sia un'istanza di Read (o generare un'eccezione).

La maggior parte dei tipi e delle strutture dati incorporati nella libreria standard hanno le istanze Show e Read definite; se stai componendo parti da esse, il tuo tipo ha anche le istanze Show e Read definite.

type Table = [(String, String)] 

load :: (Read a) => FilePath -> IO a 
load f = do s <- readFile f 
      return (read s) 

save :: (Show a) => a -> FilePath -> IO() 
save x f = writeFile f (show x) 

Se Table erano un tipo di dati, si deve chiedere per le istanze, ma è possibile richiedere che il compilatore li derivare automaticamente per voi.

data Table = Table [(String, String)] 
    deriving (Read, Show) 

A volte questo non è possibile e devi definire le tue istanze.

instance Show Table where 
    showsPrec p x = ... 
instance Read Table where 
    readsPrec p x = ... 

Ma questo non dovrebbe essere comune.

3

Con la funzione corrente si ha un problema quando le stringhe nella lista contengono "," o ")" perché ciò rende impossibile scoprire dove finisce una stringa quando si tenta di leggere nuovamente i dati. Dovresti sfuggire a questi personaggi in qualche modo ogni volta che appaiono in una stringa.

E 'molto più facile da usare show e read per convertire i dati in stringhe e allora a farlo da soli:

save :: Table -> IO() 
save zs = writeFile "database.txt" (show zs) 

show sfugge caratteri speciali e si assicura i dati sono in un formato che può essere analizzato da read. Per caricare i dati, leggere il file in una stringa e passarlo a read per convertirlo nella struttura dati desiderata.

4

L'approccio show/read funziona correttamente, lo uso anch'esso, ma solo per valori piccoli. Sui valori più grandi e complessi read sarà molto lento.

Questo esempio inventato dimostra la cattiva performance del read:

data RevList a = (RevList a) :< a | Nil 
    deriving (Show, Read) 

ghci> read "(((((((((((((((Nil)))))))))))))))" :: RevList Int 

Inoltre, read non sarà in grado di leggere alcune espressioni Haskell valide, specialmente quelli che utilizzano i costruttori infissa (come il :< nel mio esempio) . La ragione di ciò è che read non è a conoscenza della fissità degli operatori.Questo è anche il motivo per cui show $ Nil :< 1 :< 2 :< 3 genererà molte parentesi apparentemente ridondanti.

Se si desidera avere la serializzazione per valori più grandi, suggerirei di utilizzare qualche altra libreria come Data.Binary. Questo sarà un po 'più complesso di un semplice show, principalmente a causa della mancanza di deriving Binary. Tuttavia, ci sono varie soluzioni di programmazione generiche per darti sostituti di deriving -like.

Conclusione: Direi, utilizzare la soluzione show/read fino a raggiungere i suoi limiti (probabilmente una volta di iniziare a costruire applicazioni reali), quindi iniziare a guardare qualcosa di più scalabile (ma anche più complesso) come dati. Binario.


Nota a margine: per chi è interessato ai parser e alle cose più avanzate di Haskell; Gli esempi che ho fornito provengono dal documento: Haskel Do You Read Me?, su un'alternativa, fastread -like function.

+0

Per derivare Binary, http://repetae.net/computer/haskell/DrIFT/ viene in mente. Detto questo, è certamente possibile leggere e mostrare le istanze di RevList in questo caso semplice, e sono d'accordo che OP dovrebbe rimanere semplice fino a quando la scalabilità non diventa un problema. – ephemient

Problemi correlati