2009-05-06 2 views
8

나는 [(String, String)] 형식의 튜플 목록을 가지고 있으며 목록의 내용을 텍스트 파일에 쓰고 다른 함수를 작성하는 함수가 필요하다. 이 텍스트 파일을 동일한 튜플 목록으로 읽습니다. 절약 기능을 위해 내가 무엇을 얻었 는가 :하스켈 : 텍스트 파일을 쓰고 원래 형식으로 다시 파싱

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 

이 하스켈의 표준 "직렬화"방법이 있습니다. show :: (Show a) => a -> StringShow의 인스턴스 인 모든 것을 문자열로 바꿀 수 있으며 read :: (Read a) => String -> a은 문자열을 Read의 인스턴스로 바꿀 수 있습니다 (또는 예외를 throw 함).

표준 라이브러리의 기본 제공 유형 및 데이터 구조는 대부분 ShowRead 인스턴스가 정의되어 있습니다. 그들로부터 파트를 작성하는 경우, 유형에 Show W Read 인스턴스가 정의되어 있습니다. Table는 데이터 형식이었다

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) 

경우, 인스턴스를 요청해야하지만, 당신은 컴파일러가 자동으로 당신을 위해 그 (것)들을 도출하도록 요청할 수 있습니다.

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은 매우 느립니다. 또한

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

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

read 일부 유효한 하스켈 식, (내 예에서 :< 등) 중위 생성자를 사용, 특히 사람을 읽을 수 없습니다 :

이 인위적인 예는 read의 나쁜 성능을 보여줍니다 . 그 이유는 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

관련 문제