2011-12-14 119 views
24

在Haskell中這樣做的正確方法是什麼?刪除文件,如果它存在

if exists "foo.txt" then delete "foo.txt" 
doSomethingElse 

到目前爲止,我有:

import System.Directory 
main = do 
     filename <- getFileNameSomehow 
     fileExists <- doesFileExist filename 
     if fileExists 
      then removeFile filename 
      ??? 
     doSomethingElse 
+4

`doesFileExist`函數實際上是一個競爭條件的邀請。它不應該存在。 – augustss 2011-12-14 12:20:27

+7

@augustss:我們如何將它重命名爲`didFileExistLastTimeIChecked`? – 2011-12-14 18:11:21

+4

我建議`didFileNotNeverExist`。 – ehird 2011-12-14 18:34:24

回答

44

你會更好刪除文件,如果不存在,它只是恢復:

import Prelude hiding (catch) 
import System.Directory 
import Control.Exception 
import System.IO.Error hiding (catch) 

removeIfExists :: FilePath -> IO() 
removeIfExists fileName = removeFile fileName `catch` handleExists 
    where handleExists e 
      | isDoesNotExistError e = return() 
      | otherwise = throwIO e 

這就避免了有人刪除的文件中的競爭條件的代碼檢查它是否存在並刪除它。無論如何,這可能無關緊要,但無論如何,這都是不錯的做法。

請注意import Prelude hiding (catch)行 - 這是因爲Prelude包含異常處理中的舊功能,現在不贊成使用Control.Exception,它也有一個名爲catch的函數;進口產品線只是隱藏了Prelude的catch而非Control.Exception

但是,這仍然留下您更基本的基本問題:您如何在IO中編寫條件?

那麼,在這種情況下,就足夠了簡單地做

when fileExists $ removeFile filename 

(使用Control.Monad.when)。但是這裏通常在Haskell中查看類型是很有幫助的。

條件的兩個分支必須具有相同的類型。所以填寫

if fileExists 
    then removeFile filename 
    else ??? 

我們應該看看removeFile filename的類型;無論???是什麼,它必須具有相同的類型。

System.Directory.removeFile的類型爲FilePath -> IO(),所以removeFile filename的類型爲IO()。所以我們想要的是一個IO行爲,其結果類型爲(),它什麼都不做。

嗯,return目的是構造一個沒有效果的行爲,並且僅返回恆定值,並return()具有用於此的權利類型:IO()(或更一般地,(Monad m) => m())。所以???return()(你可以看到我在上面改進的代碼片段中使用,當removeFile因文件不存在而失敗時不做任何事情)。

(順便說一句,你現在應該能夠實現whenreturn()的幫助,這是非常簡單的:))

,如果你發現很難進入的東西Haskell的方式不要擔心起初 - 它會自然而然地出現,而當它發生時,這是非常有益的。 :)

10

注: ehird的回答讓關於比賽條件非常好點應該牢記讀我的答案,而忽略了當。請注意,問題中提出的命令性僞代碼也會遇到同樣的問題。)

什麼定義了文件名?它是在程序中提供的,還是由用戶提供的?在你的命令式僞代碼中,它是程序中的一個常量字符串。我假設你希望用戶通過將它作爲第一個命令行參數傳遞給程序來提供它。

那麼我建議是這樣的:

import Control.Monad  
import System.Directory 
import System.Environment 

doSomethingElse :: IO() 

main = do 
    args <- getArgs 
    fileExists <- doesFileExist (head args) 
    when fileExists (removeFile (head args)) 
    doSomethingElse 

(正如你看到的,我說的doSomethingElse類型簽名,以避免混淆)。

我輸入System.EnvironmentgetArgs函數。如果所討論的文件是由一個常量字符串(比如在你的命令性僞代碼中)給出的,只要刪除所有的參數就可以了,並且在我有head args的地方填入常量字符串。

Control.Monad被導入以獲得when函數。請注意,這個有用的功能是而不是的關鍵字(如if),但是一個普通的功能。讓我們來看看它的類型:

when :: Monad m => Bool -> m() -> m() 

在你的情況mIO,所以你能想到的when爲需要Bool和IO動作和執行只有在BoolTrue動作的功能。當然你可以用if來解決你的問題,但在你的情況下,when會讀得更清晰。至少我是這麼認爲的。

附錄:如果你像我起初,得到的感覺,when是一些神奇的和困難的機器,這是非常有益的嘗試自己定義的功能。我向你保證,它的死簡單...

相關問題