在 Haskell 中使用 do 语句
Using the do statement in Haskell
终于学习如何在 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)
但这只会引入更多令人困惑的编译器错误。
您的 do
语句中的第一行失败,因为您正试图在 IO [String]
上使用 tail
。您需要 fmap
tail
函数:
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"
(row - 1)*(col - 1)
的 waffles
函数似乎与我认为类型签名应该是什么无关。尝试从类型签名开始并从他们的构建 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
终于学习如何在 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)
但这只会引入更多令人困惑的编译器错误。
您的 do
语句中的第一行失败,因为您正试图在 IO [String]
上使用 tail
。您需要 fmap
tail
函数:
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"
(row - 1)*(col - 1)
的 waffles
函数似乎与我认为类型签名应该是什么无关。尝试从类型签名开始并从他们的构建 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