Reputation: 301
I'm trying to write a function isums
that reads n
numbers from user and returns their sum. Also, after each number, the sum up to that number is printed. I have this so far:
isums :: Int -> IO Int
isums n = do
num <- readLn
putStrLn (show (num + sum))
sum <- isums (n - 1)
return (num + sum)
Also I'm not using IORef.
Upvotes: 3
Views: 1422
Reputation: 2578
Here is one more solution: it builds on the last version of the @Willem's one, but instead of using a list of ()
's (which is a bit underwhelming) as a fuel for the loop (foldM
) it applies a list of actions for reading the values.
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'
Here replicate n readLn
creates a list of actions, each of which reads an integer. These actions are not evaluated till the go
is called during the looping by means of foldM
. The fact that we can create such a list without performing actual reading stems from the Haskell's laziness.
Upvotes: 2
Reputation: 6037
This would probably be easiest to express using a helper function, as the extra requirement of printing the partial sums after each input adds a bit of extra clutter:
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'
What you're doing is kinda like a fold (look at foldM
), only instead of traversing a list, you're getting the values "to be folded" from IO. If we had a function:
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)
Then we could write this as:
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'
Which is a bit nicer (and more reusable) as it lets us separate the general behaviour (accM
) from the specific behaviour (helper
).
Upvotes: 3
Reputation: 476534
You can use replicateM :: Applicative m => Int -> m a -> m [a]
for this:
import Control.Monad(replicateM)
isums :: (Read n, Num n) => Int -> IO n
isums n = do
numbers <- replicateM n readLn
return (sum numbers)
So here we repeat readLn
the given number of times, and then we return the sum
of the numbers
list.
An equivalent can be achieved with an fmap
:
import Control.Monad(replicateM)
isums :: (Read n, Num n) => Int -> IO n
isums n = fmap sum (replicateM n readLn)
Or even pointfree (and pointless):
import Control.Monad(replicateM)
isums :: (Read n, Num n) => Int -> IO n
isums = fmap sum . flip replicateM readLn
We can also produce a list of partial sums with scanl
:
import Control.Monad(replicateM)
isums :: (Read n, Num n) => Int -> IO [n]
isums = fmap (scanl (+) 0) . flip replicateM readLn
and then later process the list, or in case we need to print these, and return the last one, we can perform a mapM
on that list, like:
import Control.Monad(replicateM)
isums :: (Read n, Num n) => Int -> IO ()
isums n = fmap (scanl (+) 0) (replicateM n readLn) >>= mapM_ print
Or in case we need to print the partial sums:
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
Upvotes: 2