I/O Loop in Haskell

2019-09-21 07:09发布

问题:

I am currently working on this Haskell problem and I seem to be stuck.

Write a function, evalpoly, that will ask a user for the degree of a single variable polynomial, then read in the coefficients for the polynomial (from highest power to lowest), then for a value, and will output the value of the polynomial evaluated at that value. As an example run:

> evalpoly
What is the degree of the polynomial: 3
What is the x^3 coefficient: 1.0
What is the x^2 coefficient: - 2.0
What is the x^1 coefficient: 0
What is the x^0 coefficient: 10.0
What value do you want to evaluate at: -1.0
The value of the polynomial is 7.0

As of now, I have this:

evalpoly :: IO ()
evalpoly  = putStr "What is the degree of the polynomial: " >>
          getLine >>= \xs ->
          putStr "What is the x^" >>
          putStr (show xs) >>
          putStr " coefficient: " >>
          putStrLn ""

How would I go about adding the loop and calculations?

回答1:

Warning:

I spoil this completely so feel free to stop at any point and try to go on yourself


Instead of pushing it all into this single function I will instead break this down into smaller tasks/functions.

So let's start with this.

1. Input

On obvious part is to ask for an value - and if we are on it we can make sure that the user input is any good (I am using Text.Read.readMaybe for this:

query :: Read a => String -> IO a
query prompt = do
  putStr $ prompt ++ ": "
  val <- readMaybe <$> getLine
  case val of
    Nothing -> do
      putStrLn "Sorry that's a wrong value - please reenter"
      query prompt
    Just v -> return v

please note that I appended the ": " part already so you don't have to do this for your prompts


having this all the questions to your user become almost trivial:

queryDegree :: IO Int
queryDegree = query "What is the degree of the polynomial"

queryCoef :: Int -> IO (Int, Double)
queryCoef i = do
  c <- query prompt
  return (i,c)
  where prompt = "What is the x^" ++ show i ++ " coefficient"

queryPoint :: IO Double
queryPoint = query "What value do you want to evaluate at"

please note that I provide the powers together with the coefficients - this make the calculation a bit easier but is not strictly necessary here I guess (you could argue that this is more than the function should do at this point and later use zip to get the powers too)


Asking all the inputs is now really easy once you've seen mapM and what it can do - it's the point where you usually would want to write a loop:

queryPoly :: IO [(Int, Double)]
queryPoly = do
  n <- queryDegree
  mapM queryCoef [n,n-1..0]

2. Evaluation

Do evaluate this I just need to evaluate each term at the given point (that is each power, coefficient pair in the list) - which you can do using map - after we just need to sum this all up (sum can do this):

evaluate :: Double -> [(Int, Double)] -> Double
evaluate x = sum . map (\ (i,c) -> c*x^i)

3. Output

Is rather boring:

presentResult :: Double -> IO ()
presentResult v = putStrLn $ "The vaule of the polynomial is " ++ show v

4. Getting it all together

I just have to ask for the inputs, evaluate the value and then present it:

evalpoly :: IO ()
evalpoly = do
  p <- queryPoly
  x <- queryPoint
  presentResult $ evaluate x p

5. Test-Run

Here is an example run

What is the degree of the polynomial: 3
What is the x^3 coefficient: 1.0
What is the x^2 coefficient: -2.0
What is the x^1 coefficient: Hallo
Sorry that's a wrong value - please reenter
What is the x^1 coefficient: 0
What is the x^0 coefficient: 10.0
What value do you want to evaluate at: -1.0
The vaule of the polynomial is 7.0

complete Code

Note that I like to enter the no-buffering because I run into trouble on Windows occasionally if I don't have it - you probably can live without

module Main where

import Control.Monad (mapM)
import Text.Read (readMaybe)
import System.IO (BufferMode(..), stdout, hSetBuffering)

query :: Read a => String -> IO a
query prompt = do
  putStr $ prompt ++ ": "
  val <- readMaybe <$> getLine
  case val of
    Nothing -> do
      putStrLn "Sorry that's a wrong value - please reenter"
      query prompt
    Just v -> return v

queryDegree :: IO Int
queryDegree = query "What is the degree of the polynomial"

queryCoef :: Int -> IO (Int, Double)
queryCoef i = do
  c <- query prompt
  return (fromIntegral i,c)
  where prompt = "What is the x^" ++ show i ++ " coefficient"

queryPoint :: IO Double
queryPoint = query "What value do you want to evaluate at"

queryPoly :: IO [(Int, Double)]
queryPoly = do
  n <- queryDegree
  mapM queryCoef [n,n-1..0]

evaluate :: Double -> [(Int, Double)] -> Double
evaluate x = sum . map (\ (i,c) -> c*x^i)

presentResult :: Double -> IO ()
presentResult v = putStrLn $ "The vaule of the polynomial is " ++ show v

evalpoly :: IO ()
evalpoly = do
  p <- queryPoly
  x <- queryPoint
  presentResult $ evaluate x p