はい。これは少し圧倒されているように見えるかもしれませんが、Parsecのようなパーサーコンビネーターライブラリを使用すると、コードをきちんと書くことができます。例えば
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
説明として、(角かっこなしで)のmonom
ようなものを解析し、タプルのリストを取得し、その後に操作文字の1つ、続いて別のを解析し、適切なデータコンストラクターを使用して適切なインスタンス(行)を作成します。2x^3
operations
term
term
fromJust $ lookup c ops
term
パーサーは、角monom
かっこで囲まれた操作の1つまたは1つを解析します。term'
文字列全体を解析します(つまり、パーサーが文字列の最後まで実行されることを確認します)。readTerm
パーサーの単なる「よりクリーンな」バージョンです。
いくつかの例:
> 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)
上記は非常に基本的なバージョンであり、簡単に拡張して、たとえば、coef
用語をオプションにして、x^2
として解析しMonom 1 2
たり、power
用語をオプションにして、として2x
解析したりすることができMonom 2 1
ます。(このoption
関数は、この特定の変更に非常に役立ち、1行または2行だけ追加します。)
(注:これは、アプリケーションスタイルで記述された方が効率的でエレガントな場合があります。
import Control.Applicative
monom = Monom <$> int <* string "x^" <*> int
しかし、これは変更を加えるときに少し厄介になる可能性があります。)