2

I got a problem with the implementation of the readsPrec to parse an input for following datastructure:

data Term = Monom Int Int
          | Addition Term Term
          | Subtraction Term Term
          | Multiplication Term Term
          | Division Term Term

I already implemented an instance of show which makes a monom look like:

let k  = Monom 2 3
Main.show k

returns:

(2x^3)

and

let m = Addition k k
Main.show m

returns:

(2x^3)+(2x^3)

Meanwhile I'm sitting like 5 hours around with the task and I really haven't any clue how to deal with it. My first approach looked like this:

instance Read Term where
  readsPrec _ inp = let[(a,b)] = lex inp in
   case a of
   "x" -> readsPrec 0 b
   "^" -> [(Monom 1 (read b::Int), "")]
   c  -> let[(d, "")] = readsPrec 0 b in
     [(Monom (read c::Int) ((\(Monom x y) -> y) d), "")]

At first I felt very happy until I noticed that this code doesn't work for anything else than Monom. Anyone got an better approach?

RodrigoDela
  • 1,706
  • 12
  • 26
  • 1
    I suggest you take a look at [this answer](http://stackoverflow.com/a/6794085/1147955) and consider just `deriving` Read and Show for your data type. Of course, you still might want to parse input (and show output) in a format more convenient than `Addition (Monom 1 3) (Monom 2 3)`. Is that the case? If so, you could use some parser generator or a parsing library such as Parsec. But it's not 100% clear what you want to do. – Rafael Caetano Oct 22 '12 at 09:30

1 Answers1

2

Yes. This might seem a little overpowered, but using a parser combinator library like Parsec would allow you to write the code neatly. E.g.

import Text.ParserCombinators.Parsec
import Data.Maybe

monom, term :: Parser Term
operations :: [(Char,(Term -> Term -> Term))] -> Parser Term

int :: Parser Int
int = fmap read $ many1 digit

monom = do
         coef <- int
         string "x^"
         power <- int
         return $ Monom coef power


operations ops = do
                   a <- term
                   c <- choice . map (char . fst) $ ops
                   b <- term
                   return $ (fromJust $ lookup c ops) a b

term = do
        char '('
        x <- monom <|> (operations [('+', Addition), ('-', Subtraction), ('*', Multiplication), ('/', Division)])
        char ')'
        return x

term' = do 
         x <- term
         eof 
         return x

readTerm :: String -> Term
readTerm string = case parse term' "" string  of
                        Left err -> error . show $ err
                        Right term -> term

As an explanation, monom parses something like 2x^3 (without brackets), operations takes a list of tuples and parses a term followed by one of the operation characters, followed by another term, and then uses the appropriate data constructor to make the right instance (the fromJust $ lookup c ops line).

The term parser parses either a monom or one of the operations, surrounded by brackets. term' parses a whole string (i.e. makes sure that the parser runs to the end of the string). readTerm is just a "cleaner" version of the parser.

Some examples:

> readTerm "(2x^3)"
Monom 2 3
> readTerm "((2x^3)+(2x^3))"
Addition (Monom 2 3) (Monom 2 3)
> readTerm "(((2x^3)+(2x^3))*(2x^3))"
Multiplication (Addition (Monom 2 3) (Monom 2 3)) (Monom 2 3)

The above is a very basic version, and can easily be extended to (for example) make the coef term optional, so that x^2 parses as Monom 1 2, or make the power term optional so that 2x parses as Monom 2 1. (The option function is extremely useful for this specific modification, and only adds 1 or 2 more lines.)

(Note. this might be more efficient and elegant written in an applicative style, e.g.

import Control.Applicative

monom = Monom <$> int <* string "x^" <*> int

but this can get a bit unweildy when making modifications.)

huon
  • 94,605
  • 21
  • 231
  • 225
  • Wow, thank you! This solution seems to work very well. Unfortunatly it doesn't meet my requirement to implement the solution using readsPrec. Do you see any possibility to implement this solution with readsPrec? I really appreciate your help! – RodrigoDela Oct 22 '12 at 10:55
  • I was asked to implement it that way. Does it matter? I also want to improve my understanding of the class Read itself... – RodrigoDela Oct 22 '12 at 17:53
  • @RodrigoDela: writing your own `readsPrec` is quite complicated, to put it mildly! Have a look [here](http://hackage.haskell.org/packages/archive/base/latest/doc/html/Prelude.html#t:Read) to see how to parse a simpler type with values like `Leaf 3 :^: (Leaf 4 :^: Leaf 5)`, maybe you can adapt that to multiple operations? – yatima2975 Oct 22 '12 at 18:53
  • I just took a look at your link. Could you explain what actually this line does: `("Leaf",s) <- lex r` ? – RodrigoDela Oct 22 '12 at 20:27