在Haskell制作电话簿

在Haskell制作电话簿,haskell,io,monads,Haskell,Io,Monads,我有两个文件,内容如下: File 1: Tom 965432145 Bill 932121234 File 2: Steve 923432323 Tom 933232323 我想合并它们,并将结果输出写入一个名为“out.txt”的文件。我编写这个函数是为了处理重复项(当同一个名称多次出现时,它会选择进入最终文件的数字) 该函数称为选择: choosing :: [String] −> Int −> Int −> Int choosing ("Na

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

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
以下是我迄今为止根据tips所做的工作:

我把问题分解成小函数,这样更容易解决

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
import Text.Printf
导入文本.Parsec
导入Text.Parsec.String
选择um1 num2
|num2`div`100000000==2=num2
|否则=num1
parseNameNumber::解析器(字符串,整数)
parseNameNumber=do
空间
姓名打印资源
快速排序::Ord a=>[a]->[a]
快速排序[]=[]
快速排序(p:xs)=(较小的快速排序)+[p]+(较大的快速排序)
哪里
较小=过滤器(=p)xs
合并条目::[(字符串,Int)]->[(字符串,Int)]->[(字符串,Int)]
合并条目[]y=y
合并条目x[]=x
合并条目xl@(x@(xname,xphone):xs)yl@(y@(yname,yphone):ys)
|xnameyname=y:mergeEntries xs yl
序列化条目::[(Int,Char)]->[Char]
serializeEntries=concatMap(uncurry$printf“%s%d\n”)项
main=do

entries1好的,首先我不理解选择
的功能。你能用简单的英语解释一下它是如何选择数字的吗?我这样问是因为你们有两个相互矛盾的定义。你的第一个定义是:

choosing :: [String] −> Int −> Int −> Int
choosing ("Name_of_person":_) num1 _ = num1 
choosing _ num1 num2
    | num2 ‘div‘ 100000000 == 2 = num2
    | otherwise = num1
英语中的意思是“如果人名以字面字符串
name\u of_person
开头,请始终选择第一个数字。否则,如果第二个数字在200000000…29999999范围内,请选择第二个数字;如果不是,请选择第一个数字”。那是。。。奇怪,但也许有一个很好的理由

但是,当您布置整个模块时,您不会这样做。在这里,您有两个完整的
选择
,实际上是说“始终选择第一个数字”,因为第一个模式(
选择名称num1
)总是匹配的

因此,首先,找出您想用
选择
做什么。我怀疑你想做的是注释掉第一个定义

现在,关于合并。文件是否按人名排序?不然后,您需要在读取列表后对其进行排序,以便识别两个文件之间何时存在匹配的名称。列表排序后,您必须进行排序合并:

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
mergeEntries[]y=y
合并条目x[]=x
合并条目xl@(x@(xname,xphone):xs)yl@(y@(yname,yphone):ys)
|xnameyname=--您填写这部分
或者,您可以使用比列表更好的数据结构(如
Map
)来代替排序,并使用
fromListWith
toList
。但是,如果这是作业,则可能不在练习参数范围内


用于序列化。。。我想,一旦你做了你需要做的事情,把所有的空格都加进去,你就会没事的。记住,名字和数字之间有一个空格,数字后面有一个换行符。

很抱歉给你带来不便@BartekBanachewicz,我已经改了:)对不起,是的,选择的第一行应该被省略,当不同文件中有相同名称的条目时,只需选择一个数字。这是mergeEntries的正确实现吗?需要完成什么
|xname==yname=选择xname x y:mergeEntries xs yl
|xname>yname=y:mergeEntries xs yl
关闭,但您需要:
|xname==yname=选择xname xphone ypphone:mergeEntries xs ys
,然后还要选择
|xname>yname=y:mergeEntries xl ys
xl
yl
xs
ys
变量之间的差异是显著的,值得仔细考虑。