【问题标题】:How do I get a search match from a list of strings in Haskell?如何从 Haskell 中的字符串列表中获取搜索匹配?
【发布时间】:2011-08-16 11:07:34
【问题描述】:

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

【问题讨论】:

  • list.txt的内容是怎么格式化的?我猜你想过滤包含单词 name 的行?

标签: string list haskell io


【解决方案1】:

要做的第一件事,最重要的第一原则,是尽可能多地从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 -&gt; [String] -&gt; Bool 的类型与frankJ :: String -&gt; String -&gt; 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 -&gt; String -&gt; 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 使用。

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

【讨论】:

    【解决方案2】:

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

    filter (isInfixOf name) list
    

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-08-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-10-10
      • 2014-03-29
      相关资源
      最近更新 更多