2016-05-20 27 views
3

Ich lerne Haskell und ich muss zwei Dateien vergleichen. Ich habe keine Funktion gefunden, die das tut, also habe ich es selbst codiert. Unten ist die Funktion, die ich mir ausgedacht habe.Wie zwei Dateien in Haskell zu vergleichen

cmpFiles :: FilePath -> FilePath -> IO Bool 
cmpFiles a b = withBinaryFile a ReadMode $ \ha -> 
       withBinaryFile b ReadMode $ \hb -> 
       fix (\loop -> do 
        isEofA <- hIsEOF ha 
        isEofB <- hIsEOF hb 

        if | isEofA && isEofB -> return True    -- both files reached EOF 
         | isEofA || isEofB -> return False   -- only one reached EOF 
         | otherwise  -> do      -- read content 
               x <- hGet ha 4028  -- TODO: How to use a constant? 
               y <- hGet hb 4028  -- TODO: How to use a constant? 
               if x /= y 
               then return False -- different content 
               else loop   -- same content, contunue... 
       ) 

Meine Fragen sind:

  1. Ist dieser Code idiomatische? Es sieht eher zwingend als funktional aus.
  2. Ist dieser Code effizient (Layz IO Probleme mit großen Dateien, Leistung ...)?
  3. Gibt es eine kompaktere Art, es zu schreiben?
+0

Es ist nicht klar, ob Sie faule oder strikte Bytestrings verwenden (zeigen Sie immer Ihre Importe!) Ich denke, Ihr Programm wird nur mit strikten Bytestrings funktionieren, 'withBinaryFile' und Lazy IO mischen sich nicht gut. –

+0

Ihre Funktion ist zu groß. Ein einfacher Schritt wäre, eine separate Funktion zu schreiben, die zwei * Handles * benötigt. 'fix' ist selten notwendig oder hilfreich zum Schreiben von klarem Code; explizite Rekursion ist normalerweise besser. – dfeuer

Antwort

6

Wie wäre es

cmpFiles a b = do 
    aContents <- readFile a 
    bContents <- readFile b 
    return (aContents == bContents) 
+1

Sie möchten vielleicht angeben, dass dies die 'readFile' in' Data.ByteString.Lazy' sein soll. Beide funktionieren, aber die übliche könnte Speicher für große Dateien ersticken. Die 'String'-Version könnte durch die Binärdaten verwirrt sein. – jamshidh

+2

Was denkst du über die Farbe dieses Bikeshops? '' 'cmpFiles = liftA2 (==)' on' readFile''' –

0

Sie sogar einen Einzeiler draus machen können:

cmpFiles a b = liftM2 (==) (readFile a) (readFile b) 

Dieses ist tatsächlich äquivalent zu Reid Barton-Lösung. Equivalent ist kein Wiesel Wort hier, wenn Sie die Definition von liftM2 von hackage

nehmen
liftM2 f m1 m2 = do { x1 <- m1; x2 <- m2; return (f x1 x2) } 

und legen (==) und die readFile s Sie sind sofort zur Stelle.

Faulheit ist Ihr Freund in Haskell. The documentation of readFile gibt an, dass die Eingabe gelesen wird träge, d.h. nur auf Anforderung. == ist auch faul. So liest die gesamte liftM22 ... die Dateien nur so lange, bis sie einen Unterschied findet.

+0

Faulheit ist dein Freund - aber der wichtige Teil dieser Lösung ist nicht Faulheit, es ist der schlecht benannte Lazy IO. Lazy IO ist auch dein Freund [aber nur manchmal] (http://stackoverflow.com/a/6669453/791604). –

+0

Ich graben in den Code von (==) und es scheint zu vergleichen Stück von Bytes, so denke ich, es ist in der Tat faul und daher meine Sorgen der Operation zu laden die gesamte Datei vor dem Vergleich waren nicht korrekt. –