Как получить соответствие для поиска из списка строк в Haskell? - PullRequest
3 голосов
/ 01 мая 2011

Как получить соответствие поиска из списка строк в 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 ]

12 голосов
/ 01 мая 2011

Первое, что нужно сделать, крайне важный первый принцип, - это извлечь как можно больше мыслей из main или из IO.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"

В любом случае, мы сейчас действительно на земле Хаскелла: теперь проблема в том, чтобы понятьчто такое чистая функция :

 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] -> Bool с типом frankJ :: String -> String -> String.Мы хотим вписать его в наше определение frankJ.

Таким образом, чтобы «обеспечить ввод» для matchClean, нам нужна функция, которая выводит нас из длинной строки с символами новой строки в список строк (имена), которые нужны 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!"

Теперь у нас есть материалы, которые мы можем составить в разумного кандидата на функцию 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 библиотеки.Отличный и простой Data.List.Split от Hackage также может быть полезен, но я не уверен, что он может быть использован Hugs, который вы могли бы использовать.

Замечу, что вы используете устаревшие имена для импортированных модулей.То, что я написал, использует только Prelude, поэтому в импорте нет необходимости, но другие модули теперь называются «System.IO», «Data.List» и «Control.Monad» в соответствии с иерархической системой именования.Интересно, используете ли вы старый учебник или руководство.Может быть, приятный сайт «Learn You a Haskell» будет лучше?Он подтверждает, что использует ghc, но я думаю, что это не сильно повлияет.

3 голосов
/ 01 мая 2011

Если вы не хотите список всех строк в вашем list.txt, которые содержат имя, Вы можете просто использовать

filter (isInfixOf name) list

но я не уверен, правильно ли я понял ваш вопрос.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...