简体   繁体   English

在Haskell与IO monad斗争

[英]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: 回答您的更新代码:

  1. 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 映射到每一行。

  2. tuplesToStrings could be writen much simpler as tuplesToStrings = map tupleToString tuplesToStrings可以写得更简单,因为tuplesToStrings = map tupleToString

  3. 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) ). 它的类型不匹配的返回类型parseLineparseLine返回的列表(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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM