2009-05-06 6 views
8

それらを解析し、その後、別の関数このテキストファイルをタプルの同じリストとして読み込みます。保存機能のために私が持っているものは次のとおりです。ハスケル:テキストファイルの書き込みと私は[(文字列、文字列)]形式のタプルのリストを持っていると私はテキストファイルにリストの内容を書き込むための機能を必要とする元の形式に

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

これはテキストファイルに適した形式ですか?次に、そのテキストファイルを読み込んでタプルのリストに戻すにはどうしたらいいですか?あなたの現在の機能で

答えて

12

、要するに

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 

、これらは、Haskellでは、標準の "直列化" の方法です。 show :: (Show a) => a -> Stringは、文字列にShowのインスタンスである何かを変えることができ、かつread :: (Read a) => String -> aRead(または例外をスロー)のインスタンスであるものに文字列を変えることができます。

標準ライブラリのほとんどの組み込み型およびデータ構造体には、ShowおよびReadのインスタンスが定義されています。あなたがそれらの部品を構成している場合、あなたのタイプも定義ShowReadインスタンスがあります。

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) 

Tableがデータ型だった場合は、インスタンスをお願いする必要がありますが、あなたは、コンパイラが自動的にあなたのためにそれらを導出することを要求することができます。

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

時にはそれは不可能ですし、あなた自身のインスタンスを定義する必要があります。

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

しかし、それは一般的ではありません。

3

あなたがリスト内の文字列が含まれている問題を抱えている「」または 『)』それはあなたがもう一度データを読み取るしようとすると、文字列が終わる場所を見つけるためにそれを不可能にするため。これらの文字は、文字列に表示されるときはいつでもエスケープする必要があります。

それはあなた自身でそれを行うに戻った文字列にデータを変換するために、showreadを使用する方がはるかに簡単だと:

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

showに特殊文字をエスケープし、データをその可能な形式であることを確認しますreadによって解析されます。データをロードするには、文字列にファイルを読み込み、ご希望のデータ構造に変換するreadにこれを渡します。 Preludeで定義され

4

show/readアプローチは問題なく動作しますが、小さな値の場合にのみ使用します。大きく、より複雑な値では、readは非常に遅くなります。

この不自然な例はreadの悪いパフォーマンスを示しています。また

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

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

を、readは(私の例では:<のように)、中置のコンストラクタを使用し、特にもの、いくつかの有効なHaskellの表情を読み取ることができません。 。この理由は、readは、事業者の固定性を認識していないということです。これはまた、show $ Nil :< 1 :< 2 :< 3が一見重複した括弧をたくさん生成する理由です。

より大きな値をシリアル化する場合は、Data.Binaryのような他のライブラリを使用することをお勧めします。これは、単純にshowより幾分複雑です。主にderiving Binaryがないためです。しかし、derivingのようなサロゲートを提供するためのさまざまな汎用プログラミングソリューションがあります。

結論:私は、その後、データのような、よりスケーラブル(だけでなく、より複雑な)何かを探し始める、あなたがその限界に達するまで(あなたは、実際のアプリケーションの構築を開始し、おそらく一度)show/readソリューションを使用して、と言うだろう。バイナリ。


サイドノート:パーサーとより高度なハスケルのものに興味のある人に。私が与えた例は、別紙のHaskel Do You Read Me?の論文から来たものです。fastreadのような機能です。

+0

バイナリを導出するために、http://repetae.net/computer/haskell/DrIFT/が気に入っています。つまり、この単純なケースでは、RevListのインスタンスをもっとスマートに読み書きすることは可能ですが、スケーラビリティが問題になるまで、OPは単純に固執する必要があります。 – ephemient

関連する問題