[英]Struggling with IO monad in Haskell
So i've got two files with the following content: 所以我有两个文件,其内容如下:
File 1:
Tom 965432145
Bill 932121234
File 2:
Steve 923432323
Tom 933232323
and i want to merge them and write the resulting output to a file named 'out.txt'. 我想将它们合并,并将结果输出到名为“ out.txt”的文件中。 i wrote this function to deal with duplicates (when the same name appears more than once, it choses what number goes into the final file).
我编写了此函数来处理重复项(当同一个名字出现多次时,它选择将哪个数字输入最终文件)。
the function is called choosing: 该函数称为选择:
choosing :: [String] −> Int −> Int −> Int
choosing ("Name_of_person":_) num1 _ = num1
choosing _ num1 num2
| num2 ‘div‘ 100000000 == 2 = num2
| otherwise = num1
Here's my attempt at this: 这是我的尝试:
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
However this kinda of makes sense to me, it doesn't compile and after a while trying to debug this i'm now starting to think there's some serious mistakes here, so i would really appreciate your help on this. 但是,这对我来说很有意义,它无法编译,并且在尝试调试此功能后一段时间,我现在开始认为这里存在一些严重错误,因此,非常感谢您的帮助。
Here's my attempt at something simpler first: 这是我首先尝试的一些简单的尝试:
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
but it's not working either... 但这也不起作用...
UPDATED: 更新:
So basically i've been trying to solve my problem, with all the tips i got, i managed to do this: 所以基本上我一直在尝试解决我的问题,并获得了所有提示,我设法做到了:
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
I think the problem is that your thinking too imperative. 我认为问题在于您的思考势在必行。 In Haskell you usually split your solution in small blocks, and each block does only one thing.
在Haskell中,您通常将解决方案分成几个小块,每个块只能做一件事。 It's much easier to reason about one small block, and it's also easier to reuse that block in other parts.
推理一个小块要容易得多,也可以在其他部分重用该块。 For example, here's how I would breakdown the code for this problem:
例如,这是我如何分解此问题的代码:
parseEntries :: String -> [(String, Int)]
A function that receives the content of a file and parses the entries. 接收文件内容并解析条目的函数。 It the case of the content of
in1.txt
it would return [("Tom", 965432145), ("Bill", 932121234)]
如果是
in1.txt
的内容,它将返回[("Tom", 965432145), ("Bill", 932121234)]
mergeEntries :: [(String, Int)] -> [(String, Int)] -> [(String, Int)]
A function that receives entries from two files and merges them. 从两个文件接收条目并将其合并的函数。
serializeEntries :: [(String, Int)] -> String
A function that receives entries, merges them into a single string so that it can be writen to a file. 接收条目的函数,将它们合并为单个字符串,以便可以将其写入文件。
Having defined these functions, main
becomes as simple as this: 定义了这些功能后,
main
变得如此简单:
main = do
entries1 <- fmap parseEntries $ readFile "in1.txt"
entries2 <- fmap parseEntries $ readFile "in2.txt"
writeFile "out.txt" $ serializeEntries $ mergeEntries entries1 entries2
Answering to your updated code: 回答您的更新代码:
Now that you have a function to parse a line, parseEntries
is easy. 既然您具有解析行的功能,则
parseEntries
变得很简单。 Use the lines
function to split the content by lines, then map parseLine
to each line. 使用
lines
函数将内容按行分割,然后将 parseLine
映射到每一行。
tuplesToStrings
could be writen much simpler as tuplesToStrings = map tupleToString
tuplesToStrings
可以写得更简单,因为tuplesToStrings = map tupleToString
I don't see how tuplesToString
will help you. 我看不到
tuplesToString
将如何帮助您。 Its type doesn't match the type returned by parseLine
( parseLine
returns a list of (String, Int)
while tuplesToString
expects a list of (Int, Char)
). 它的类型不匹配的返回类型
parseLine
( parseLine
返回的列表(String, Int)
而tuplesToString
预计的列表(Int, Char)
)。 And it doesn't even insert spaces between words or between lines. 而且它甚至不会在单词之间或行之间插入空格。 Here's a possible implementation for
serializeEntries
(using Text.Printf module): 这是
serializeEntries
的可能实现(使用Text.Printf模块):
serializeEntries entries = concatMap (uncurry $ printf "%s %d\\n") entries
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.