Right now I'm trying to make a program that gives the sum of all numbers entered thus far. currently I have this start of the code:
import System.Exit
main :: IO()
main = do
a <- getLine
if a == "EXIT_PROGRAM"
then
exitWith ExitSuccess
else
print(a)
main
It's working fine right now but you can probably tell it's not what I want. To get this I would have to save the previous sum and then add it to a, then save that and repeat the function. How can I do this? Thank you.
CodePudding user response:
Found a way, probably should have figured it out sooner. I made a separate function that unlike main could take input. then I fed the product into it.
import System.Exit
multiplier :: Integer -> IO()
multiplier prevProduct = do
a <- getLine
if a == "EXIT_PROGRAM"
then
exitWith ExitSuccess
else
return()
let b = (read a :: Integer) * prevProduct
print(b)
multiplier b
main :: IO()
main = do
multiplier 1
also I changed it so it multiplied instead of added
CodePudding user response:
The implementation suggested in another answer has the correct behaviour, but strikes me as perhaps not entirely idiomatic. In Haskell return
doesn't mean what it does in most other languages, so the use of return ()
seems odd. Likewise, using exitWith
strikes me as using exceptions for flow control - a technique that is frowned upon in most languages (not just Haskell).
Furthermore, the program will not be able to handle any errant input such as foo
, exit
, or even EXIT PROGRAM
. In all such cases, it's going to crash:
> multiplier 1
2
2
3
6
EXIT PROGRAM
*** Exception: Prelude.read: no parse
As chi suggests in a comment, using recursion would be more idiomatic, but in Haskell, you often do that by having a base case that exits cleanly.
The desired behaviour is almost a Read Eval Print Loop (REPL), except that the evaluation function is given on beforehand. So perhaps, readPrintLoop
might be an appropriate name. Here's one way to do it:
readPrintLoop :: (Show a, Read b) => (a -> b -> a) -> a -> IO ()
readPrintLoop eval acc = do
s <- getLine
let m = readMaybe s
case m of
Just x -> do
let y = eval acc x
print y
readPrintLoop eval y
Nothing -> return ()
This action uses readMaybe from Text.Read
to parse the input with the option of failing gracefully. As a consequence, any input that doesn't parse (such as foo
, exit
or EXIT_PROGRAM
) will exit the loop.
It first reads the input using getLine
, parses with readMaybe
, and then pattern-matches the Maybe
value m
.
If m
is Nothing
, the action exits cleanly with ()
.
If m
is a Just
value, it uses eval
(a function argument) to calculate the new accumulated value, prints it, and recurses with the new accumulator.
Since the OP describes accumulating a sum, and the answer from the same user accumulates a product, I've made the readPrintLoop
flexible by enabling the caller to define the function being used.
You can even use it to accumulate decimal numbers as well (since any Read
instance will do):
> readPrintLoop ( ) 0.0
1.1
1.1
2.3
3.4
exit
>
If, on the other hand, you want main
to accumulate an integer product, you may define it like this:
main :: IO ()
main = do
readPrintLoop (*) 1