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

fra*_*nkJ 3 string io haskell list

如何从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 
Run Code Online (Sandbox Code Playgroud)

app*_*ive 12

首先要做的是,最重要的第一个原则是尽可能多地考虑main或者尽可能多地进行思考IO.main应该尽可能包含所有的IO,也许只是IO装饰着您在模块中其他地方定义的纯术语.你getLines不必要地混合它们.

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

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

- 或者可能IO是我们得到的所有其他方面更严格的隔离:

main = 
   do putStrLn greeting
      name <- getContents
      names <- readFile nameFile
      putStrLn (frankJ name names) 
Run Code Online (Sandbox Code Playgroud)

连同'纯'术语:

greeting, nameFile :: String
greeting = "What is your name?"
nameFile = "names.txt"
Run Code Online (Sandbox Code Playgroud)

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

 frankJ :: String -> String -> String
Run Code Online (Sandbox Code Playgroud)

应该.

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

 match :: String -> [String] -> Bool
 match name namelist = name `elem` namelist  
 -- pretty clever, that!
Run Code Online (Sandbox Code Playgroud)

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

 clean :: String -> String
 clean =  reverse . omitSpaces . reverse . omitSpaces
   where omitSpaces = dropWhile (== ' ')
Run Code Online (Sandbox Code Playgroud)

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

 matchClean :: String -> [String] -> Bool
 matchClean name namelist = match (clean name) (map clean namelist)
Run Code Online (Sandbox Code Playgroud)

现在,我们需要按照类型,搞清楚如何适应的,比如类型,matchClean:: String -> [String] -> Bool与的frankJ :: String -> String -> String.我们希望将其纳入我们的定义中frankJ.

因此,为了"提供输入" matchClean,我们需要一个函数将我们从带有换行符的长字符串带到需要的stings(名称)列表matchClean:这就是Prelude函数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!"
Run Code Online (Sandbox Code Playgroud)

现在我们有了一些材料,我们可以将这些材料组合成一个合理的候选函数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
Run Code Online (Sandbox Code Playgroud)

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

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!"
Run Code Online (Sandbox Code Playgroud)

和...一起

frankJpersonal name = personalResponse name . matchClean name . lines
Run Code Online (Sandbox Code Playgroud)

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

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