2016-11-23 8 views
2

私の目標は、+、 - 、*、/、^、否定を実装し、PEMDASに続くText.ParserCombinators.ReadPを使用してHaskellで電卓を構築することです。まず、数式(MathExp)への文字列入力を解析する必要があります。次のように私が持っているコードの一部は、次のとおりです。括弧と否定を読む

import Control.Applicative hiding (many) 
import Control.Monad 
import Data.Char 
import Data.List 
import Data.Ord 
import Text.ParserCombinators.Readp 
type Name = String 
type Number = Int  

data MathExp 
    = Number Number 
    | Neg MathExp 
    | Plus MathExp MathExp 
    | Minus MathExp MathExp 
    | Mult MathExp MathExp 
    | Div MathExp MathExp 
    | Pow MathExp MathExp 
    | Var String 
    deriving (Eq, Show) 

parseNumber :: ReadP MathExp 
parseNumber = do 
    skipSpaces 
    x <- munch1 isDigit 
    return (Number (read x :: Int)) 

parsePlus :: ReadP (MathExp -> MathExp -> MathExp) 
parsePlus = do 
    x <- char '+' 
    return Plus 

parseMinus :: ReadP (MathExp -> MathExp -> MathExp) 
parseMinus = do 
    skipSpaces 
    x <- char '-' 
    return Minus 

parsePlusMinus = choice [parsePlus, parseMinus] --parse both-- 

parseMult :: ReadP (MathExp -> MathExp -> MathExp) 
parseMult = do 
    x <- char '*' 
    return Mult 

parseDiv :: ReadP (MathExp -> MathExp -> MathExp) 
parseDiv = do 
    x <- char '/' 
    return Div 

parseMultDiv = choice [parseMult, parseDiv] --parse both M,D-- 
parsePow :: ReadP (MathExp -> MathExp -> MathExp) 
parsePow = do 
    x <- char '^' 
    return Pow 

parseNeg :: ReadP MathExp 
parseNeg = undefined 
parseParens = undefined 

は、私が正しい順序で結合性と優先順位を実装するために、chainl1chainr1を使用して、私が現在持っているパーサを組み合わせる問題はないが、私はどのように見当もつかない否定と括弧を正しく実装する。

否定が-の記号であることはわかっていて、整数、かっこ、または変数(文字列)の前に来る可能性があります。変数は計算機の別の部分です(私は問題を抱えていません)。さらに、否定は空白を含むことができます。たとえば、1+2* - 3は、この電卓の有効な文字列です。

+0

"PEMDASを" 本当の言葉ではありません。あなたは「操作の順序」を意味しますか? – dfeuer

+0

はい、操作の順序。 – Harambe17

+0

こんにちはCS 161このページを見つけた学生検索すると、1.オンラインで宿題を投稿しないでください。 2.あなたの宿題をするためにSOの貢献者に頼らないでください。問題がある場合は、Piazzaに投稿してください。だから私たちはそれを使用しています。そしてコラボレーションポリシーに慣れてください:http://cmsc-16100.cs.uchicago.edu/2017/policies.php私たちはあなたが代入とのオリジナルのやりとりを実証するためにあなたが入ったソリューションを期待します。ご不明な点がございましたら、メールでお問い合わせください。平和、あなたのインストラクター –

答えて

3

あなたはこのように行うことができます。

import Control.Applicative 
import Data.Char 
import Text.ParserCombinators.ReadP 

type Name = String 
type Number = Int 

data MathExp 
    = Number Number 
    | Neg MathExp 
    | Plus MathExp MathExp 
    | Minus MathExp MathExp 
    | Mult MathExp MathExp 
    | Div MathExp MathExp 
    | Pow MathExp MathExp 
    | Var Name 
    deriving (Eq, Show) 

runParser :: ReadP a -> String -> Maybe a 
runParser p s = 
    case readP_to_S p s of 
     [(x, [])] -> Just x 
     _   -> Nothing 

mathExp = mathExp' <* skipSpaces <* eof 
mathExp' = term `chainl1` addop 
term  = factor `chainl1` mulop 
factor = expo `chainr1` expop 
expo  = number <|> var <|> parens mathExp' <|> neg expo 

number = skipSpaces *> (Number . read <$> munch1 isDigit) 
var = skipSpaces *> (Var <$> munch1 isAlpha) 
addop = skipSpaces *> (Plus <$ char '+' <|> Minus <$ char '-') 
mulop = skipSpaces *> (Mult <$ char '*' <|> Div <$ char '/') 
expop = skipSpaces *> (Pow <$ char '^') 
neg p = skipSpaces *> (Neg <$> (char '-' *> p)) 

parens = between (skipSpaces *> char '(') (skipSpaces *> char ')') 

main = print $ runParser mathExp "1+2* - 3" 

出力は次のようになります。

Just (Plus (Number 1) (Mult (Number 2) (Neg (Number 3)))) 
+0

もう1つの質問:次の文字列を読むパーサを実装したいと思った場合: "x + y * zeeのput(x、y、zee)=(1,2,3)"を変数を宣言/使用していることを示す "put"と "for"という単語があり、これを[["x"、 "y"、 "zee"] [Number 1、Number 2、Number 3]( (Var "x")(Mult(Var "y")(Var "zee"))) 'これをどうやって行うのですか? – Harambe17

+0

まず、構文に関して、あなたのタスクに抽象構文木(AST)を設計する必要があります。次に、ASTの解析について考えることができます。私はあなたの例の構文と意味を理解していないので、もっとあなたを助けることができません。 – freestyle