在Haskell中,我想读取一个文件然后写入。我需要严格的注释吗? [英] In Haskell, I want to read a file and then write to it. Do I need strictness annotation?

查看:227
本文介绍了在Haskell中,我想读取一个文件然后写入。我需要严格的注释吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

对于Haskell来说,还是相当新的。我想读取一个文件的内容,对它做一些可能涉及到IO的事情(现在使用putStrLn),然后将新内容写入同一个文件。



我想出了:

  doit :: String  - > IO()
doit file = do
contents < - withFile tagfile ReadMode $ \h - > hGetContents h
putStrLn内容
withFile标记文件WriteMode $ \h - > hPutStrLn h新内容

然而,由于懒惰,这是行不通的。文件内容不打印。我找到了这篇文章,对此进行了解释。



提出的解决方案是在 withFile 中包含 putStrLn

  doit :: String  - > IO()
doit file = do
withFile tagfile ReadMode $ \h - > do
contents< - hGetContents h
putStrLn contents
withFile tagfile WriteMode $ \h - > hPutStrLn hnew content

这个方法可行,但这不是我想要做的。在我的操作最终将取代 putStrLn 可能会很长,我不希望保持整个文件打开。一般来说,我只是希望能够获得文件内容,然后在处理该内容之前将其关闭。



我提出的解决方案如下: / p>

  doit :: String  - > IO()
doit file = do
c < - newIORef
withFile tagfile ReadMode $ \h - >做
a< - hGetContents h
writeIORef c $! a
d < - readIORef c
putStrLn d
withFile tagfile WriteMode $ \h - > hPutStrLn hTest

然而,我发现这个很长,有点混乱。我不认为我需要一个 IORef 才能得到一个值,但是我需要放置来放置文件内容。另外,对于 writeIORef ,如果没有严格标注 $!,它仍然不起作用。我认为 IORef s本质上并不严格?

任何人都可以推荐一个更好,更短的方法来做到这一点保持我想要的语义?



谢谢!

第一个程序不起作用,在执行传递给它的IO操作后, withFile 关闭文件。在你的情况下,IO操作是 hGetContents ,它不会立即读取文件,而只是要求读取它的内容。当你试图打印文件的内容时, withFile 已经关闭了文件,所以读取失败(默默地)。



你可以通过不重新发明轮子来简单地使用 readFile writeFile
$ $ p $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ b $ $ b writeFile文件新内容

但是,假设您希望新内容依赖旧内容。那么你通常不能简单地做

$ do $ file $ do
内容< - readFile文件
writeFile文件$进程内容

因为 writeFile 可能会影响 readFile 返回的内容(记住,它还没有真正读取文件)。或者,根据您的操作系统,您可能无法打开相同的文件,以便在两个单独的句柄上进行读取和写入。简单但丑陋的解决方法是

$ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ b $内容< - readFile文件
长度内容`seq`(writeFile file $ process contents)

这将强制 readFile 读取整个文件并在 writeFile 操作可以开始之前关闭它。


Still quite new to Haskell..

I want to read the contents of a file, do something with it possibly involving IO (using putStrLn for now) and then write new contents to the same file.

I came up with:

doit :: String -> IO ()
doit file = do
    contents <- withFile tagfile ReadMode $ \h -> hGetContents h
    putStrLn contents
    withFile tagfile WriteMode $ \h -> hPutStrLn h "new content"

However this doesn't work due to laziness. The file contents are not printed. I found this post which explains it well.

The solution proposed there is to include putStrLn within the withFile:

doit :: String -> IO ()
doit file = do
    withFile tagfile ReadMode $ \h -> do
        contents <- hGetContents h
        putStrLn contents
    withFile tagfile WriteMode $ \h -> hPutStrLn h "new content"

This works, but it's not what I want to do. The operation in I will eventually replace putStrLn might be long, I don't want to keep the file open the whole time. In general I just want to be able to get the file content out and then close it before working with that content.

The solution I came up with is the following:

doit :: String -> IO ()
doit file = do
    c <- newIORef ""
    withFile tagfile ReadMode $ \h -> do
        a <- hGetContents h
        writeIORef c $! a
    d <- readIORef c
    putStrLn d
    withFile tagfile WriteMode $ \h -> hPutStrLn h "Test"

However, I find this long and a bit obfuscated. I don't think I should need an IORef just to get a value out, but I needed "place" to put the file contents. Also, it still didn't work without the strictness annotation $! for writeIORef. I guess IORefs are not strict by nature?

Can anyone recommend a better, shorter way to do this while keeping my desired semantics?

Thanks!

解决方案

The reason your first program does not work is that withFile closes the file after executing the IO action passed to it. In your case, the IO action is hGetContents which does not read the file right away, but only as its contents are demanded. By the time you try to print the file's contents, withFile has already closed the file, so the read fails (silently).

You can fix this issue by not reinventing the wheel and simply using readFile and writeFile:

doit file = do
    contents <- readFile file
    putStrLn contents
    writeFile file "new content"

But suppose you want the new content to depend on the old content. Then you cannot, generally, simply do

doit file = do
    contents <- readFile file
    writeFile file $ process contents

because the writeFile may affect what the readFile returns (remember, it has not actually read the file yet). Or, depending on your operating system, you might not be able to open the same file for reading and writing on two separate handles. The simple but ugly workaround is

doit file = do
    contents <- readFile file
    length contents `seq` (writeFile file $ process contents)

which will force readFile to read the entire file and close it before the writeFile action can begin.

这篇关于在Haskell中,我想读取一个文件然后写入。我需要严格的注释吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆