[英]Struggling with IO monad in Haskell
所以我有两个文件,其内容如下:
File 1:
Tom 965432145
Bill 932121234
File 2:
Steve 923432323
Tom 933232323
我想将它们合并,并将结果输出到名为“ out.txt”的文件中。 我编写了此函数来处理重复项(当同一个名字出现多次时,它选择将哪个数字输入最终文件)。
该函数称为选择:
choosing :: [String] −> Int −> Int −> Int
choosing ("Name_of_person":_) num1 _ = num1
choosing _ num1 num2
| num2 ‘div‘ 100000000 == 2 = num2
| otherwise = num1
这是我的尝试:
import System.IO
import Data.Char
choosing :: [String] −> Int −> Int −> Int
choosing name num1 _ = num1
choosing _ num1 num2
| num2 `div` 100000000 == 2 = num2
| otherwise = num1
main :: IO ()
main = do
in1 <- openFile "in1.txt" ReadMode
in2 <- openFile "in2.txt" ReadMode
out <- openFile "out.txt" WriteMode
processData in1 in2 out
hClose in1
hClose in2
hClose out
processData :: Handle -> Handle -> Handle -> IO ()
processData in1 in2 out =
do ineof <- hIsEOF in1
ineof2 <- h2IsEOF in2
if ineof && ineof2
then return ()
else do inpStr <- hGetLine in1
inp2Str <- h2GetLine in2
num1Int <- num1GetNumber in1
num2Int <- num2GetNumber in2
if inpStr = inp2Str
then PutStrLn out (impStr choosing inpStr num1Int num2Int )
else PutStrLn out (inpStr num1Int)
PutStrLn out (inp2Str num2Int)
processData in1 in2 out
但是,这对我来说很有意义,它无法编译,并且在尝试调试此功能后一段时间,我现在开始认为这里存在一些严重错误,因此,非常感谢您的帮助。
这是我首先尝试的一些简单的尝试:
import System.IO
import Data.Char
choosing name num1 _ = num1
choosing _ num1 num2
| num2 `div` 100000000 == 2 = num2
| otherwise = num1
main :: IO ()
main = do
in1 <- openFile "in1.rtf" ReadMode
in2 <- openFile "in2.rtf" ReadMode
out <- openFile "out.rtf" WriteMode
mainloop in1 out
mainloop in2 out
hClose in1
hClose in2
hClose out
mainloop :: Handle -> Handle -> IO ()
mainloop _ out =
do ineof <- hIsEOF in
if ineof
then return ()
else do inpStr <- hGetLine in
hPutStrLn out (inpStr)
mainloop in out
但这也不起作用...
更新:
所以基本上我一直在尝试解决我的问题,并获得了所有提示,我设法做到了:
import System.IO
import Data.Char
- Main function to run the program
main = do
entries1 <- fmap parseEntries $ readFile "in1.txt"
entries2 <- fmap parseEntries $ readFile "in2.txt"
writeFile "out.txt" $ serializeEntries $ mergeEntries entries1 entries2
- Function to deal with duplicates
choosing name num1 _ = num1
choosing _ num1 num2
| num2 `div` 100000000 == 2 = num2
| otherwise = num1
- Function to read a line from a file into a tuple
Now i need help making this function 'cover' the whole file, and not just one line of it.
parseLine :: String -> (String, Int)
parseLine xs = (\(n:i:_) -> (n, read i)) (words xs)
- A function that receives entries, merges them into a single string so that it can be writen to a file.
import Data.Char
tupleToString :: (Int, Char) -> [Char]
tupleToString x = (intToDigit.fst) x:(snd x):[]
tuplesToStrings [] = []
tuplesToStrings (x:xs) = tupleToString x : tuplesToStrings xs
tuplesToString xs = (concat . tuplesToStrings) xs
我认为问题在于您的思考势在必行。 在Haskell中,您通常将解决方案分成几个小块,每个块只能做一件事。 推理一个小块要容易得多,也可以在其他部分重用该块。 例如,这是我如何分解此问题的代码:
parseEntries :: String -> [(String, Int)]
接收文件内容并解析条目的函数。 如果是in1.txt
的内容,它将返回[("Tom", 965432145), ("Bill", 932121234)]
mergeEntries :: [(String, Int)] -> [(String, Int)] -> [(String, Int)]
从两个文件接收条目并将其合并的函数。
serializeEntries :: [(String, Int)] -> String
接收条目的函数,将它们合并为单个字符串,以便可以将其写入文件。
定义了这些功能后, main
变得如此简单:
main = do
entries1 <- fmap parseEntries $ readFile "in1.txt"
entries2 <- fmap parseEntries $ readFile "in2.txt"
writeFile "out.txt" $ serializeEntries $ mergeEntries entries1 entries2
回答您的更新代码:
既然您具有解析行的功能,则parseEntries
变得很简单。 使用lines
函数将内容按行分割,然后将 parseLine
映射到每一行。
tuplesToStrings
可以写得更简单,因为tuplesToStrings = map tupleToString
我看不到tuplesToString
将如何帮助您。 它的类型不匹配的返回类型parseLine
( parseLine
返回的列表(String, Int)
而tuplesToString
预计的列表(Int, Char)
)。 而且它甚至不会在单词之间或行之间插入空格。 这是serializeEntries
的可能实现(使用Text.Printf模块):
serializeEntries entries = concatMap (uncurry $ printf "%s %d\\n") entries
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.