Haskell从用户读取n个数字并返回它们的总和

时间:2018-04-22 10:28:39

标签: haskell io sum

我正在尝试编写一个函数isums,用于从用户读取n个数字并返回它们的总和。此外,在每个数字之后,将打印该数字的总和。到目前为止我有这个:

isums :: Int -> IO Int
isums n = do
    num <- readLn
    putStrLn (show (num + sum))
    sum <- isums (n - 1)
    return (num + sum)

我也不是在使用IORef。

3 个答案:

答案 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的懒惰。