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

发布于 2024-11-04 13:06:30 字数 336 浏览 2 评论 0原文

如何从 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 

How do I get a search match from a list of strings in 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 

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

音盲 2024-11-11 13:06:30

首先要做的事情,也是最重要的首要原则,是尽可能多地从 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 领域:现在的问题是弄清楚纯函数

 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 -> [字符串] -> BoolfrankJ :: String ->字符串 ->字符串。我们希望将其放入 frankJ 的定义中。

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

但我们还需要决定如何处理 matchClean 生成的 Bool 作为值;正如我们所知,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 ->; 的合理候选者的材料。字符串 ->我们要输入到 main 中定义的 IO 机器的字符串

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 使用,您可能正在使用 Hugs。

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

The first thing to do, the all-important first principle, is to get as much of the thinking out of main or out of IO as possible. main should where possible contain all the IO and maybe nothing but IO decorated with pure terms you define elsewhere in the module. Your getLines is mixing them unnecessarily.

So, to get that out of the way, we should have a main that is something like

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

-- or maybe the more austere segregation of IO from all else that we get from:

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

together with the 'pure' terms:

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

Either way, we are now really in Haskell-land: the problem is now to figure out what the pure function:

 frankJ :: String -> String -> String

should be.

We might start with a simple matching function: we get a match when the first string appears on a list of strings:

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

or we might want to normalize a bit, so that white space at the beginning and end of the name we are given and the names on the list doesn't affect the match. Here's a rather shabby way to do that:

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

Then we can improve on our old match, i.e. elem:

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

Now we need to follow the types, figuring out how to fit the type of, say, matchClean:: String -> [String] -> Bool with that of frankJ :: String -> String -> String. We want to fit it inside our definition of frankJ.

Thus, to 'provide input' for matchClean, we need a function to take us from a long string with newlines to the list of stings (the names) that matchClean needs: that's the Prelude function lines.

But we also need to decide what to do with the Bool that matchClean yields as value; frankJ, as we have it, returns a String. Let us continue with simple-minded decomposition of the problem:

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!"

Now we have materials we can compose into a reasonable candidate for the function frankJ :: String -> String -> String that we are feeding into our IO machine defined in 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

So here, almost everything is a matter of pure functions, and it is easy to see how to emend things for further refinement. For example, maybe the name entered and the lines of the text file should be further normalized. Internals spaces should be restricted to one space, before the comparison. Or maybe there is a comma in lines on the list since people are listed as "lastname, firstname", etc. etc. Or maybe we want the response function to use the person's name:

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!"

together with

frankJpersonal name = personalResponse name . matchClean name . lines

Of course there are a million ways of going about this. For example, there are regex libraries. The excellent and simple Data.List.Split from Hackage might also be of use, but I'm not sure it can be used by Hugs, which you might be using.

I note that you are using old-fashioned names for the imported modules. What I have written uses only the Prelude so imports are unnecessary, but the other modules are now called "System.IO", "Data.List" and "Control.Monad" in accordance with the hierarhical naming system. I wonder if you are using an old tutorial or manual. Maybe the pleasant 'Learn You a Haskell' site would be better? He affirms he's using ghc but I think that won't affect much.

爱*していゐ 2024-11-11 13:06:30

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

filter (isInfixOf name) list

,但我不确定我是否正确理解您的问题。

If you wan't a list of all lines in your list.txt that contain the name,
you can simply use

filter (isInfixOf name) list

but I'm not sure if I understood your question correct.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文