首页 > 解决方案 > 在 Haskell 中使用 do 语句

问题描述

终于学会了如何在 Haskell 中使用 monad!

我想读取一个文件testInput,删除第一行,将该函数waffles应用于每隔一行,然后将结果保存在一个文件中output.txt

我写了以下代码:

main = do
    contents <- tail . fmap lines . readFile $ "testInput"
    result <- fmap waffles contents
    writeFile "output.txt" $ concat result

waffles row col = (row - 1)*(col - 1)

可悲的是编译器抱怨:

waffles.hs:3:41:
    Couldn't match type ‘IO String’ with ‘[String]’
    Expected type: FilePath -> [String]
      Actual type: FilePath -> IO String
    In the second argument of ‘(.)’, namely ‘readFile’
    In the second argument of ‘(.)’, namely ‘fmap lines . readFile’

waffles.hs:5:9:
    Couldn't match expected type ‘[b]’ with actual type ‘IO ()’
    Relevant bindings include program :: [b] (bound at waffles.hs:2:1)
    In a stmt of a 'do' block: writeFile "output.txt" $ concat result
    In the expression:
      do { contents <- tail . fmap lines . readFile $ "testInput";
           result <- fmap waffles contents;
           writeFile "output.txt" $ concat result }
    In an equation for ‘program’:
        program
          = do { contents <- tail . fmap lines . readFile $ "testInput";
                 result <- fmap waffles contents;
                 writeFile "output.txt" $ concat result }
Failed, modules loaded: none.

我发现这个错误非常令人生畏。你能帮我调试一下吗?

我也很感激代码风格的建议!

编辑:我忘了拆分文件的行并将它们转换为整数。我尝试如下解决:

main = do
    contents <- tail . fmap lines . readFile $ "testInput"
    contents <- fmap read . words contents
    result <- fmap waffles contents
    writeFile "output.txt" $ concat result

waffles row col = (row - 1)*(col - 1)

但这只会引入更多令人困惑的编译器错误。

标签: haskelliomonads

解决方案


语句中的第一行do失败,因为您试图tailIO [String]. 你需要fmaptail功能:

contents <- fmap tail . fmap lines . readFile $ "testInput"
-- or....
contents <- fmap (tail . lines) . readFile $ "testInput"

现在您需要一种方法从contents. 您可以为此定义一个简单的everyOther函数:

everyOther :: [a] -> [a]
everyOther (x:_:xs) = x : everyOther xs
everyOther _        = []

现在您可以将其链接到您fmap的第一行:

contents <- fmap (everyOther . tail . lines) . readFile $ "testInput"

您的waffles功能(row - 1)*(col - 1)似乎与我认为的类型签名无关。尝试从类型签名开始并waffles从中构建。根据您的描述,您只是向函数提供每隔一行,因此它应该具有签名:

waffles :: String -> String

鉴于 的类型签名waffles,您可以通过以下方式应用它:

let result = fmap waffles contents

输出的另一件事:concat将所有行混合在一起。您可能希望在其中换行,因此您可能想要使用unlines

main = do
    contents <- fmap (everyOther . tail . lines) . readFile $ "testInput"
    let result = fmap waffles contents
    writeFile "output.txt" $ unlines result

推荐阅读