如何在Haskell中检查两个文件是否相等

4
我正在学习Haskell,需要比较两个文件。我没有找到可以做到这一点的函数,所以我自己编写了一个。以下是我想出来的函数。
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...
                 )

我的问题是:

  1. 这段代码是否符合惯用法? 它看起来非常命令式而不是函数式。
  2. 这段代码是否高效(大文件的Layz IO问题,性能...)?
  3. 有没有更紧凑的编写方式?

1
不清楚你是否使用惰性或严格的字节串(请始终显示您的导入!)这个 对我来说是惯用的惰性IO。我认为你的程序只能使用严格的字节串,withBinaryFile和惰性IO不兼容。 - n. m.
1
你的函数太大了。一个简单的步骤是编写一个单独的函数,该函数接受两个句柄fix很少必要或有助于编写清晰的代码;显式递归通常更好。 - dfeuer
3个回答

6

你好

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

2
你可能想要指明这应该是 Data.ByteString.Lazy 中的 readFile 函数……两种方式都可以工作,但通常的方式可能会对大文件消耗过多内存。String 版本可能会对二进制数据产生混淆。 - jamshidh
2
你认为这个自行车棚的颜色怎么样?cmpFiles = liftA2 (==) `on` readFile - Daniel Wagner

0

你甚至可以将其变成一行代码:

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

这个实际上等同于Reid Barton的解决方案。等同在这里不是一个含糊其辞的词,如果你从hackage中获取liftM2的定义。

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

插入(==)readFile,你就在那里了。

懒惰是Haskell中的好朋友。readFile的文档指出输入是惰性读取的,即只有在需要时才读取。 ==也是惰性的。因此,整个liftM22 ...仅在找到差异时才读取文件。


懒惰是你的朋友 - 但这个解决方案的重要部分不是懒惰,而是名字不好的懒惰 IO。懒惰 IO 也是你的朋友,但只有在某些情况下。 - Daniel Wagner
我深入研究了(==)的代码,它似乎正在比较字节块,所以我认为它确实是惰性的,因此我的担忧,即操作在比较之前加载整个文件,是不正确的。 - And Pos

0
cmpFiles a b = (==) <$> readFile a <*> readFile b

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接