如何从Haskell中的字符串列表中获取搜索匹配?

时间:2011-05-01 13:38:36

标签: string list haskell io

如何从Haskell中的字符串列表中获取搜索匹配?

module Main
    where
import List
import IO
import Monad

getLines = liftM lines . readFile

main = do
    putStrLn "Please enter your name: "
    name <- getLine
    list <- getLines "list.txt"
   -- mapM_ putStrLn list -- this part is to list out the input of lists 

2 个答案:

答案 0 :(得分:12)

要做的第一件事,即最重要的第一原则,就是尽可能多地从mainIO中获得思考。 main应尽可能包含所有IO,除了IO之外,您可以使用您在模块中其他位置定义的纯术语进行修饰。您的getLines正在不必要地混合它们。

所以,为了解决这个问题,我们应该有main类似

main = 
   do putStrLn "What is your name?"
      name <- getContents
      names <- readFile "names.txt"
      putStrLn (frankJ name names)

- 或者可能是IO与我们得到的所有其他问题更为严峻的隔离:

main = 
   do putStrLn greeting
      name <- getContents
      names <- readFile nameFile
      putStrLn (frankJ name names) 

与'纯'术语一起使用:

greeting, nameFile :: String
greeting = "What is your name?"
nameFile = "names.txt"

无论哪种方式,我们现在真的在Haskell-land:问题是现在要弄清楚什么是纯函数

 frankJ :: String -> String -> String

应该是。

我们可以从简单的匹配函数开始:当第一个字符串出现在字符串列表中时,我们得到匹配:

 match :: String -> [String] -> Bool
 match name namelist = name `elem` namelist  
 -- pretty clever, that!

或者我们可能想要标准化一点,以便在给出的名称的开头和结尾处的空格以及列表上的名称不会影响匹配。这是一个相当简陋的方法:

 clean :: String -> String
 clean =  reverse . omitSpaces . reverse . omitSpaces
   where omitSpaces = dropWhile (== ' ')

然后我们可以改进旧的match,即elem

 matchClean :: String -> [String] -> Bool
 matchClean name namelist = match (clean name) (map clean namelist)

现在我们需要遵循这些类型,找出如何使matchClean:: String -> [String] -> BoolfrankJ :: String -> String -> String的类型相匹配。我们希望将其纳入frankJ

的定义中

因此,要为matchClean“提供输入”,我们需要一个函数将我们从带有换行符的长字符串带到matchClean需要的stings(名称)列表:这就是前奏函数lines

但我们还需要决定如何处理Bool matchClean作为值产生的结果;我们拥有的frankJ会返回String。让我们继续简单地分解问题:

response :: Bool -> String
response False = "We're sorry, your name does not appear on the list, please leave."
response True = "Hey, you're on the A-list, welcome!"

现在我们有一些材料,我们可以合成一个合理的候选函数frankJ :: String -> String -> String,我们正在为IO中定义的main机器提供信息:

frankJ name nametext = response (matchClean name (lines nametext))

-- or maybe the fancier:  
-- frankJ name = response . matchClean name . lines
-- given a name, this 
--     - pipes the nametext through the lines function, splitting it,
--     - decides whether the given name matches, and then 
--     - calculates the 'response' string

所以在这里,几乎所有事情都是纯粹的功能问题,很容易看出如何进一步完善事物。例如,可能输入的名称和文本文件的行应进一步标准化。在比较之前,内部空间应限制在一个空间。或者列表中的行可能有逗号,因为人们被列为“姓氏,名字”等等。或者我们希望响应函数使用该人的姓名:

personalResponse :: String -> Bool -> String
personalResponse name False = name ++ " is a loser, as far as I can tell, get out!"
personalResponse name True  = "Ah, our old friend " ++ name ++ "! Welcome!"

一起
frankJpersonal name = personalResponse name . matchClean name . lines

当然,有一百万种方法可以解决这个问题。例如,有regex个库。来自Hackage的优秀且简单的Data.List.Split也可能有用,但我不确定它可以被Hugs使用,你可能正在使用它。

我注意到您为导入的模块使用了老式名称。我所写的仅使用Prelude,因此不需要导入,但其他模块现在根据层次命名系统称为“System.IO”,“Data.List”和“Control.Monad”。我想知道你是否使用旧的教程或手册。也许令人愉快的'Learn You a Haskell'网站会更好吗?他肯定他正在使用ghc,但我认为这不会影响太多。

答案 1 :(得分:3)

如果您不想列出list.txt中包含该名称的所有行的列表, 你可以简单地使用

filter (isInfixOf name) list

但我不确定我是否理解你的问题。