【问题标题】:Making a PhoneBook in Haskell在 Haskell 中制作电话簿
【发布时间】:2013-12-11 12:48:17
【问题描述】:

所以我有两个文件,内容如下:

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

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

【问题讨论】:

  • 很抱歉给您带来不便@BartekBanachewicz,我已经改了:)

标签: haskell io monads


【解决方案1】:

好的,首先我不明白函数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,而不是排序。但是,如果这是可能不在练习范围内的家庭作业。

对于序列化...我认为,一旦您完成了在其中添加所有空格所需的操作 - 请记住,名称和数字之间的空格以及数字后的换行符。

【讨论】:

  • 对不起,是的,第一行选择应该省略,只是在不同文件中存在同名条目时选择一个数字
  • 这是 mergeEntries 的正确实现吗,需要完成什么? | xname == yname = choosing xname x y : mergeEntries xs yl| xname &gt; yname = y : mergeEntries xs yl
  • 关闭,但您想要:| xname == yname = choosing xname xphone yphone : mergeEntries xs ys,然后还有| xname &gt; yname = y : mergeEntries xl ysxlylxsys 变量之间的差异是显着的,值得仔细考虑。
猜你喜欢
  • 1970-01-01
  • 2017-06-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-11-09
  • 1970-01-01
  • 1970-01-01
  • 2021-07-23
相关资源
最近更新 更多