arithmetic
This commit is contained in:
		
							parent
							
								
									a7309c7473
								
							
						
					
					
						commit
						4790712b97
					
				
					 2 changed files with 86 additions and 0 deletions
				
			
		
							
								
								
									
										86
									
								
								arithmetic/Main.hs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										86
									
								
								arithmetic/Main.hs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,86 @@ | |||
| module Main where | ||||
| 
 | ||||
| import Data.Char | ||||
| import qualified Data.Map as M | ||||
| 
 | ||||
| data Token = Number Double | ParenOpen | ParenClose | AddOp | MulOp  | DivOp | SubOp deriving (Show, Eq) | ||||
| 
 | ||||
| isOp:: Token -> Bool | ||||
| isOp AddOp = True | ||||
| isOp MulOp = True | ||||
| isOp DivOp = True | ||||
| isOp SubOp = True | ||||
| isOp _ = False | ||||
| 
 | ||||
| scanNumber:: String -> (Double, String) | ||||
| scanNumber xs = (num, str) where  | ||||
|         (n, str) = span isNumber xs | ||||
|         num = read n :: Double | ||||
| 
 | ||||
| opmap:: M.Map Char Token | ||||
| opmap = M.fromList [ ('+', AddOp), ('*', MulOp), ('/', DivOp), ('(', ParenOpen), ('-', SubOp), (')', ParenClose)] | ||||
| 
 | ||||
| tokenize:: String -> [Maybe Token] | ||||
| tokenize s = loop s [] where | ||||
|     loop str tokens | ||||
|         | null str = tokens | ||||
|         | isNumber $ head str = let | ||||
|                     (num, str') = scanNumber str | ||||
|                     tokens' = tokens ++ [Just (Number num)] | ||||
|                 in loop str' tokens' | ||||
|         -- Skip whitespaces | ||||
|         | (\c -> c == ' ') $ head str = loop (tail str) tokens | ||||
|         -- Otherwise lookup for op | ||||
|         | otherwise = loop (tail str) (tokens ++ [M.lookup (head str) opmap]) | ||||
| 
 | ||||
| opPriority :: Token -> Int | ||||
| opPriority AddOp = 0 | ||||
| opPriority SubOp = 0 | ||||
| opPriority MulOp = 1 | ||||
| opPriority DivOp = 1 | ||||
| opPriority ParenOpen = 2 | ||||
| opPriority ParenClose = 2 | ||||
| opPriority (Number _) = 3 | ||||
| 
 | ||||
| -- Shunting yard algo | ||||
| transform:: [Maybe Token] -> [Token] | ||||
| -- tokens - stack - output | ||||
| transform ts = transform' ts [] [] where | ||||
|     transform' [] [] q = q | ||||
|     transform' [] s q = | ||||
|         if head s == ParenOpen  | ||||
|             then error "Mismatched parentheses"  | ||||
|             else transform' [] (tail s) (q ++ [head s]) | ||||
|     transform' (x:xs) s q = case x of | ||||
|         Nothing -> error "Illegal tokens" | ||||
|         (Just (Number n)) -> transform' xs s (q ++ [Number n]) | ||||
|         (Just ParenOpen) -> transform' xs (ParenOpen:s) q | ||||
|         (Just ParenClose) -> transform' xs s0 q0 where | ||||
|             s0 = tail $ dropWhile (/= ParenOpen) s | ||||
|             q0 = q ++ takeWhile (/= ParenOpen) s | ||||
|         (Just o1) -> transform' xs s1 q1 where | ||||
|             cond o2 = isOp o2 && (opPriority o1 < opPriority o2) | ||||
|             spl = span cond s | ||||
|             s1 = o1 : snd spl | ||||
|             q1 = q ++ fst spl | ||||
| 
 | ||||
| eval :: [Token] -> Token | ||||
| eval ts = head $ eval' [] ts where | ||||
|     eval' :: [Token] -> [Token] -> [Token] | ||||
|     eval' st (t:ts) = let  | ||||
|         numFromToken (Number n) = n | ||||
|         fstNum = numFromToken . head | ||||
|         sndNum = numFromToken . head . tail | ||||
|         rem = tail . tail | ||||
|         in case t of | ||||
|             Number n -> eval' (t : st) ts | ||||
|             AddOp -> eval' ((Number (fstNum st + sndNum st)) : (rem st)) ts | ||||
|             MulOp -> eval' ((Number (fstNum st * sndNum st)) : (rem st)) ts | ||||
|             DivOp -> eval' ((Number (sndNum st / fstNum st)) : (rem st)) ts | ||||
|             SubOp -> eval' ((Number (sndNum st - fstNum st)) : (rem st)) ts | ||||
|     eval' st [] = st | ||||
| 
 | ||||
| main = do | ||||
|     s <- getLine | ||||
|     let Number res = eval $ transform $ tokenize s | ||||
|     print res | ||||
		Reference in a new issue