Haskell - читать список людей - PullRequest
0 голосов
/ 06 января 2019

Проблема звучит так: напишите программу, которая читает число n, а затем n человек, для каждого человека, читает их имя и возраст, а затем возвращает самых старых людей / людей.

Пример ввода:

3
Ion Ionel Ionescu
70
Gica Petrescu
99
Mustafa ben Muhamad
7

Пример вывода

Oldest is Gica Petrescu (99 years).

Пока мой код:

readPers :: IO(String, Int)
readPers = do
    name <- getLine
    age <- readLn :: IO Int
    return (name, age)

readPerss :: (Ord t, Num t) => t -> [IO (String, Int)]
readPerss n
    | n > 0 = readPers : readPerss(n-1)
    | otherwise = []

pFunc = do
    print "Numer of persons:"
    n <- readLn :: IO Int
    let persons = readPerss n
    return persons

Сначала я читаю, затем пытаюсь составить список людей, использующих readPers и readPerss, но я застрял, я не знаю, как с этим справиться, и я предполагаю, что моя реализация пока не совершенно верно.

Как мне решить проблему?

Ответы [ 2 ]

0 голосов
/ 06 января 2019

Это то, как я всегда делаю это (не из-за проблем с кодом). Я всегда разделяю IO и логику как можно скорее. Работает отлично (если N не очень большой).

import Data.List.Split (chunksOf)

type Person = (String, Int)

main = do
    x <- getContents
    putStrLn $ program x

program :: String -> String
program s = “Oldest is “ ++ x ++ “ (“ ++ (show y) ++ “ years old).”
    where
    (x, y)  = solve persons
    persons = [(name, read age :: Int) | [name, age] <- chunksOf 2 . tail . lines $ s]

solve :: [Person] -> Person
solve ls = undefined 

Я оставляю вам undefined.

0 голосов
/ 06 января 2019

Вы очень близки! То, что вы делаете в readPerss :: (Ord t, Num t) => t -> [IO (String, Int)], возвращает список IO действий; каждое действие возвращает пару String и Int при его выполнении. В настоящее время в pFunc вы только строите этот список действий, сохраняете его в переменной с let и возвращаете его из pFunc; вы никогда не выполняете их с помощью оператора <- «bind».

Есть несколько простых способов сделать то, что вы хотите. Самое маленькое изменение в вашем коде, которое делает то, что вы хотите, это добавить sequence, который принимает контейнер действий и производит действие , которое возвращает контейнер :

sequence :: (Traversable t, Monad m) => t (m a) -> m (t a)

Здесь t равно [], m равно IO, а a равно (String, Int):

sequence :: [IO (String, Int)] -> IO [(String, Int)]

Другой способ - переписать readPerss, чтобы он выполнял действия напрямую, накапливая (String, Int) результаты в списке, а не накапливая IO действий :

readPerss :: (Ord t, Num t) => t -> IO [(String, Int)]
-- Change [IO …] to IO […]:         ~~~~~~~~~~~~~~~~~~

readPerss n
  | n > 0 = do
    pers <- readPers
    perss <- readPerss (n - 1)
    return (pers : perss)
  | otherwise = return []

Я знаю, что вы не должны использовать библиотечные функции, если это домашнее задание или упражнение, но в типичном коде «повторить x действие n раз и накапливать результаты» часто обозначают replicateM n x:

replicateM :: Applicative m => Int -> m a -> m [a]
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...