简体   繁体   中英

Haskell problems in a RPN caculator implementation

I'm trying to implement a RPN caculator in Haskell. It is an exercise from Learn You a Haskell . Here's my code:

import Data.List
solveRPN :: String -> Int
solveRPN str = head $ foldl putStack [] (words str) 
         where putStack accumulator token 
            | token == "+" = pFunction (+)
            | token == "-" = pFunction (-)
            | token == "*" = pFunction (*)
            | token == "/" = pFunction (`div`)
            | otherwise    = accumulator ++ [read token :: Float]
            where pFunction function =  (int $ init accumulator) ++ [function argu1 argu2]
                  argu1 = last accumulator
                  argu2 = last $ init accumulator

The function solveRPN first split a string into tokens. (ex: "4 3 2 + *" -> ["4","3","2","+","*"] ) Then, one by one tokens are pushed into a stack. If it meets an operator, the last two items in the stack are processed by the operator and the value yielded is then put back into the stack. When the whole list is traversed, there's only one item left in the stack, and that's the answer.

There are some problems here:

  1. In (int $ init accumulator) I want to cancel the last two elements in the stack. Is there any alternative to (int $ init accumulator) ?

  2. The code can't pass the compilation. GHC said "parse error on input ("
    on this line: | token == "/" = pFunction ( | token == "/" = pFunction ( div ) . I suspect the problem might come from pFunction . Its parameter is an operator(or can I call it a function?) and I'm not sure if "function as the parameter of a function" is legal in Haskell. Is this legal? Is there any alternative?

  3. I did some experiments in GHCi and found something strange:

 Prelude> let plus = (+) Prelude> :t (+) (+) :: Num a => a -> a -> a Prelude> :t plus plus :: Integer -> Integer -> Integer 

How come the type of plus is different from the type of (+)?

Thanks for your attention and patience. (:

In (int $ init accumulator)

Did you mean init $ init accumulator ? That being said, you could write your own dropLast2 function, which does the same as init . init init . init but traverses the list only once, something like

dropLast2 :: [a] -> [a]
dropLast2 []       = []
dropLast2 [_]      = []
dropLast2 [_,_]    = []
dropLast2 (x:xs) = x : dropLast2 xs

The code can't pass the compilation.

        | token == "/" = pFunction (`div`)

You're using backticks (`) in order to use functions with two arguments as infix functions. However, by using parenthesis around div you try to cancel it right away, which is both a little bit off and a parser error. Simply use

        | token == "/" = pFunction div

instead. However, there's one major thing of. div 's type is

div :: Integral a => a -> a -> a

However, your accumulator is list of Float . div cannot possibly work on those. However, Float is an instance of the Fractional class, so you can simply use (/) :

(/) :: Fractional a => a -> a -> a

and therefore get

        | token == "/" = pFunction (/)

I did some experiments in GHCi and found something strange

(+) is part of the Num class. Your plus isn't part of any function, and GHC tries to infer its type. And then monomorphism restriction kicks in. See Type error while writing max function for further information.

Regarding your first question:

I get the impression that you are using lists the wrong way around. If you push an element on the stack, you should prepend it to the list using the : operator. Popping the top two elements then becomes drop 2 stack , which is much nicer, I think.

Doing it this way would also be more efficient, since : is a constant time operation while ++ is linear in the size of the first argument (ie your stack's size).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM