2012-09-25 13 views
3

我正在嘗試在Haskell中進行一些編程。我試圖讀取一個文件,然後通過使用行函數將文件中的每一行放在一個列表中。下面是部分代碼:readFile後使用線條

file = "muh.rtr" 
readTrack :: String -> Track 
readTrack file = 
    do  let defFile = readFile file 
      let fileLines = lines defFile 

不過,我不斷收到此錯誤:

Parser.hs:22:39: 
    Couldn't match expected type `String' with actual type `IO String' 
    In the first argument of `lines', namely `defFile' 
    In the expression: lines defFile 
    In an equation for `fileLines': fileLines = lines defFile 

我一直在網上搜索了幾個小時,現在希望的地方找到一些答案,但我沒有那麼幸運了至今。

+1

[這裏有一個答案(HTTP://計算器。com/questions/12664050/works-in-ghci-but-not-when-loaded/12665589#12665589)我給了一個和你一樣得到相同錯誤信息的人。 – AndrewC

回答

6

類型的readFile

readFile :: FilePath -> IO String 

,所以你需要使用<-的結果綁定,你的函數必須返回IO Track

readTrack :: String -> IO Track 
readTrack file = 
    do defFile <- readFile file 
    let fileLines = lines defFile 
    ... 

我建議閱讀在Haskell IO上的一個很好的教程,例如Input and Output chapter of Learn You a Haskell for Great Good!

9

你可能想要麼是這樣的:

readTrack :: String -> IO Track 
readTrack file = do defFile <- readFile file 
        let fileLines = lines defFile 
        -- etc.... 

...或者是這樣的:

readTrack :: String -> IO Track 
readTrack file = do fileLines <- liftM lines (readFile file) 
        -- etc.... 

但你真的應該是阻止什麼,去找介紹諸如Learn You a Haskell之類的語言,並花一些時間閱讀它。

將完全由非常簡單的錯誤組成的代碼提供給GHC,然後在Stack Overflow上發佈錯誤消息不是學習的好方法。

+0

謝謝,但是我失去耐心並在這裏發佈這個問題的原因是因爲我明天有一個截止日期,晚上已經晚了,所以我已經很累了。 無論如何,它必須是「readTrack :: String - > Track」(我正在做一個小項目,說我應該實現這樣的功能) 是否沒有其他方式能夠使用「線條」 IO Track的? – user1683526

+7

@ user1683526我已經幾次看到過這種評論(「我有一份應該在明天到期的工作,需要快速的回答」),但從來沒有發現它很有說服力。你的糟糕計劃不是我們的責任。如果你需要學習一件事情以取得進展,那麼你需要了解它,無論你是否有進展的最後期限。 –

+4

@ user1683526:如果這是家庭作業的一部分,那麼你的問題*比一個最後期限要多得多。問題和你的評論都表明你不瞭解一些非常重要的基本概念。按照這個速度你不會做得很好。 –

5

readFile返回IO string。也就是說,這是一個返回字符串的IO計算。這意味着您需要使用<-而不是let來「返回」字符串。

readTrack file = 
    do 
     defFile <- readFile file 
     ... 

您可以使用let綁定的東西並不IO計算,如線的返回值,這是一個正常的字符串。

readTrack file = 
    do 
     defFile <- readFile file 
     let fileLines = lines defFile 
     ... 

最後,你需要返回你可能想嘗試像

readTrack file = 
    do 
     defFile <- readFile file 
     let fileLines = lines defFile 
     fileLines --Doesn't actually work! 

但價值很遺憾,因爲我們是一個「做」塊內,並試圖返回一元計算,我們需要將fileLines被送回IO單子(記住,出函數返回IO [String],不String

readTrack file = 
    do 
     defFile <- readFile file 
     let fileLines = lines defFile 
     return fileLines 

注意「返回「在這裏是而不是返回語句在大多數語言中通常會被找到,它不應該用在你的純函數中。

所有這些在開始時可能看起來很多。我建議你堅持純粹的功能(無需輸入和輸出/單子),直到你更好地掌握語言。

4

你不能這樣做 - 你碰到了IO monad。你需要做的是一樣的東西:

readTrack :: String -> IO Track 
readTrack file = do 
    defFile <- readFile file 
    let fileLines = lines deffile 
    ... 
    return whatever 

IO T值看作語句(而不是表達式)返回類型爲T。由於語句有副作用,但表達式不會,所以您不能將語句轉換爲表達式;類型系統強制執行此操作,這就是您的類型簽名不起作用的原因。

注意在do塊中的不同的分配狀語法:在這個例子中,foo <- bar用於IO操作,而let baz = quux語法用於純功能性的評價。這是使用monadic I/O的更多後果 - 它在Haskell的多態類型系統的完整概括性中更有意義,但是具有純粹與副作用操作的語法指示也不一定是不好的。

一般情況下,這是很好的做法,儘量保持大部分的實現在純功能性的領域:與普通功能方法來實現你的純計算,然後描述你的I/O操作的IO單子。在IO monad中編寫循環是一種常見的新手錯誤,這對於列表解析或遞歸函數更合適。

3

如果你的函數應該有類型readTrack :: String -> Track,你確定String是一個文件名嗎?也許它是數據 - 如果是這樣,請不要使用readFile。寫一些樣本數據和測試使用,如

sampleData = "2 3\n1 30 234 45\n1 2 32 4\n5 3 4 23" 

(SO上這個功課沒有使用文件IO,因爲你是我不會鏈接到它在危機和可能是另一個問題誘惑複製,並在任何情況下,如果你拒絕學習haskell至少我會迫使你提高你的StackOverflow搜索技巧:))

在任何情況下,我想你會得到更多的標記,通過解決字符串問題而不是解決IO問題。

延遲的READFILE問題,直到你已經得到了純淨版的工作,否則,你可能最終寫在大部分的IO單子你的代碼這會比需要的要複雜得多。

一個你有一個純函數readTrack :: String -> Track,你可以做

readTrackFrom :: FilePath -> IO Track 
readTrackFrom filename = fmap readTrack (readFile filename) 

現在,fmap :: Functor f => (a -> b) -> f a -> f b,所以需要純粹的功能和提升他們在像IO不同的計算上下文的工作。

由於IOFunctor(明天查看,不是今晚),我們使用它作爲(String -> Track) -> IO String -> IO Track類型。這很好,因爲readTrack :: String -> Track(readFile filename) :: IO String

如果你願意,你就可以>>= print>>= writeFile newfilename您認爲合適的。

使用後請不要忘記添加deriving Showdata Track =...,但如果您使用的是type Track = ....,則不需要。

+0

@ user1683526我相信你已經有一個多星期的作業了。下次馬上開始 - 你將有時間寫出一個非常好的解決方案,學習很多東西並消除任何困難。 Haskell是一個很好的編程語言,但你必須學習很多才能有效地使用它 - 思考更多,寫得更少,但更好。 – AndrewC