在Haskell制作电话簿

时间:2013-12-11 12:48:18

标签: haskell io monads

所以我有两个文件包含以下内容:

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 Text.Printf
import Text.Parsec
import Text.Parsec.String


choosing _ num1 num2
  | num2 `div` 100000000 == 2 = num2
  | otherwise = num1



parseNameNumber :: Parser (String, Integer)
parseNameNumber = do
spaces
name <- many1 letter
space
number <- fmap read $ many1 digit
return (name, number)

parseFile :: String -> IO ()
parseFile = do
result <- parseFromFile (parseNameNumber `sepBy` newline)
case result of
Left err  -> print err
Right res -> print res


quicksort :: Ord a => [a] -> [a]
quicksort []     = []
quicksort (p:xs) = (quicksort lesser) ++ [p] ++ (quicksort greater)
where
    lesser  = filter (< p) xs
    greater = filter (>= p) xs

mergeEntries :: [(String, Int)] -> [(String, Int)] -> [(String, Int)]
mergeEntries [] y = y
mergeEntries x [] = x
mergeEntries xl@(x@(xname, xphone):xs) yl@(y@(yname, yphone):ys)
   | xname < yname  = x : mergeEntries xs yl
   | xname == yname = choosing xname x y : mergeEntries xs yl
   | xname > yname  = y : mergeEntries xs yl


serializeEntries :: [(Int, Char)] -> [Char]
serializeEntries entries = concatMap (uncurry $ printf "%s %d\n") entries


main = do
  entries1 <- fmap parseFile $ readFile "in1.txt"
  entries2 <- fmap parseFile $ readFile "in2.txt"
  writeFile "out.txt" $ serializeEntries $ mergeEntries $ quicksort entries1 quicksort entries2

现在我认为一切都是正确的,除了我的parse函数返回IO()而不是字符串,我该如何改变呢?

1 个答案:

答案 0 :(得分:0)

好的,首先我不理解函数choosing。你能用简单的英语解释它如何选择数字吗?我问,因为那里有两个相互矛盾的定义。您所说的第一个定义是:

choosing :: [String] −> Int −> Int −> Int
choosing ("Name_of_person":_) num1 _ = num1 
choosing _ num1 num2
    | num2 ‘div‘ 100000000 == 2 = num2
    | otherwise = num1

这句用英语说的是“如果这个人的姓名以文字字符串Name_of_person开头,请始终选择第一个数字。否则,如果第二个数字在200000000 ... 299999999范围内,请选择第二个数字;如果不是,请选择第一个数字“。那是......很奇怪,但也许有充分的理由。

但是,当您放置整个模块时,不会这样做。在那里,你拥有的是choosing的两个完整定义,并且实际上是在说“总是选择第一个数字”,因为第一个模式(choosing name num1 _)总是匹配。

首先,弄清楚你想对choosing做些什么。我怀疑你想要做的是评论第一个定义。

现在,至于合并。文件是按人名排序的吗?没有?然后,您需要在读取列表后对列表进行排序,以便识别两个文件之间是否存在匹配的名称。列表排序后,您将不得不进行排序合并:

mergeEntries [] y = y
mergeEntries x [] = x
mergeEntries xl@(x@(xname, xphone):xs) yl@(y@(yname, yphone):ys)
   | xname < yname  = x : mergeEntries xs yl
   | xname == yname = -- You fill in this part, using choosing here
   | xname > yname  = -- You fill in this part

或者,除了排序之外,您可以使用比列表更好的数据结构(例如Map)并使用fromListWithtoList。但是,如果这是家庭作业,可能不在练习的参数范围内。

对于序列化...我认为一旦你做了你需要做的事情就可以添加所有空格 - 请记住,名称和数字之间的空格以及数字后面的换行符。