我正在尝试编写一个函数isums
,用于从用户读取n
个数字并返回它们的总和。此外,在每个数字之后,将打印该数字的总和。到目前为止我有这个:
isums :: Int -> IO Int
isums n = do
num <- readLn
putStrLn (show (num + sum))
sum <- isums (n - 1)
return (num + sum)
我也不是在使用IORef。
答案 0 :(得分:3)
使用辅助函数表示这可能是最容易的,因为在每次输入后打印部分和的额外要求会增加一些额外的混乱:
isums :: Int -> IO Int
isums n = helper n 0
where
helper 0 acc = return acc
helper m acc = do
x <- readLn
let x' = acc + x
print x'
helper (m - 1) x'
你正在做的事情有点像折叠(看foldM
),而不是遍历列表,你从IO获得“折叠”的值。如果我们有一个功能:
accM :: Monad m => (a -> m a) -> m a -> Int -> m a
accM f acc 0 = acc
accM f acc n = accM f (acc >>= f) (n - 1)
然后我们可以把它写成:
isums :: Int -> IO Int
isums n = accM helper (return 0) n
where
helper acc = do
x <- readLn
let x' = acc + x
print x'
return x'
哪个更好(并且更可重用)因为它允许我们将一般行为(accM
)与特定行为(helper
)分开。
答案 1 :(得分:2)
您可以使用replicateM :: Applicative m => Int -> m a -> m [a]
:
import Control.Monad(replicateM)
isums :: (Read n, Num n) => Int -> IO n
isums n = do
numbers <- replicateM n readLn
return (sum numbers)
所以我们在这里重复readLn
给定的次数,然后我们返回sum
列表的numbers
。
使用fmap
:
import Control.Monad(replicateM)
isums :: (Read n, Num n) => Int -> IO n
isums n = fmap sum (replicateM n readLn)
甚至 pointfree (以及无意义):
import Control.Monad(replicateM)
isums :: (Read n, Num n) => Int -> IO n
isums = fmap sum . flip replicateM readLn
我们还可以使用scanl
:
import Control.Monad(replicateM)
isums :: (Read n, Num n) => Int -> IO [n]
isums = fmap (scanl (+) 0) . flip replicateM readLn
然后处理列表,或者如果我们需要打印这些,并返回最后一个,我们可以在该列表上执行mapM
,如:
import Control.Monad(replicateM)
isums :: (Read n, Num n) => Int -> IO ()
isums n = fmap (scanl (+) 0) (replicateM n readLn) >>= mapM_ print
或者我们需要打印部分金额:
isums :: (Read n, Num n, Show n) => Int -> IO n
isums n = foldM f 0 (replicate n ())
where f a _ = readLn >>= \b -> let c = a + b in print c >> return c
答案 2 :(得分:2)
这是另一个解决方案:它建立在@Willem的最后一个版本上,但不是使用()
的列表(这有点令人沮丧)作为循环的燃料({ {1}})它应用了一系列用于读取值的操作。
foldM
此处import Control.Monad
isums n = foldM go 0 (replicate n readLn)
where
go s a = do
x <- a
let s' = s + x
print s'
return s'
创建一个操作列表,每个操作都读取一个整数。在循环期间通过replicate n readLn
调用go
之前,不会评估这些操作。事实上,我们可以在不执行实际阅读的情况下创建这样的列表,这源于Haskell的懒惰。