问题听起来像这样:编写一个程序,先读取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
我先读n,然后尝试使用readPers和readPerss列出人员列表,但我被困住了,从那时起我不知道如何解决这个问题,我想到目前为止我的实现还不是非常正确。
我应该如何解决问题?
答案 0 :(得分:3)
您非常亲密!您在readPerss :: (Ord t, Num t) => t -> [IO (String, Int)]
中所做的工作将返回IO
操作的列表;每个动作在执行时都会返回一对String
和Int
。当前在pFunc
中,您只是构建这个动作列表,将其存储在带有let
的变量中,然后从pFunc
返回它;您永远不会使用<-
“ bind”语句执行。
有几种简单的方法可以执行所需的操作。对您的代码所做的最小更改就是添加sequence
,它需要一个动作容器,并生成一个返回容器的动作:< / p>
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]
答案 1 :(得分:2)
这就是我一直这样做的方式(不是来自代码挑战)。我总是尽快将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
留给您。