数学パーサーを設計する最も賢い方法は何ですか?私が意味するのは、数学文字列(「2 + 3/2 +(2 * 5)」など)を取り、計算された値を返す関数ですか?私は何年も前にVB6でそれを書きましたが、それは肥大化する方法であり、あまりポータブルではありませんでした(またはその点でスマートです...)。一般的なアイデア、擬似コード、または実際のコードを歓迎します。
かなり良いアプローチには、2つのステップが含まれます。最初のステップには、 式を中置から後置に変換する (たとえば ダイクストラのシャンティングヤード )表記法が含まれます。それが終わったら、 postfix evaluator を書くのはとても簡単です。
数学パーサーの設計に関するいくつかのブログ記事を書きました。一般的な 紹介 、 文法 、 Rubyで書かれたサンプル実装 、および テストスイート に関する基本的な知識があります。おそらくあなたはこれらの資料が役に立つと思うでしょう。
いくつかのアプローチがあります。動的なコードを生成して実行することで、多くのコードを記述することなく答えを得ることができます。 .NETのランタイム生成コードで検索を実行するだけで、さまざまな例があります。
あるいは、実際のパーサーを作成して、式の評価に使用される小さな解析ツリーを生成することもできます。繰り返しますが、これは基本的な式については非常に簡単です。彼らがそこに数学パーサーを持っていると信じているので、codeplexをチェックしてください。または、例を含むBNFを検索します。コンパイラの概念を紹介するWebサイトには、これが基本的な例として含まれています。
私はこれが古いことを知っていますが、より大きなアプリの一部として電卓を開発しようとしてこれに出会い、受け入れられた答えを使用していくつかの問題に出くわしました。リンクは、この問題を理解して解決するのに非常に役立ちました。割引するべきではありません。 Android app in Javaと式「文字列」の各項目について、ユーザーが入力するときに実際にArrayListに文字列を格納しました中置から後置への変換では、ArrayList内の各文字列を反復処理し、文字列の新しく配置された後置ArrayListを評価しました。これは少数のオペランド/演算子にとっては素晴らしいことでしたが、長い計算は一貫してオフでした特に、式が非整数に評価され始めたとき Infix to Postfix conversion のリンクでは、スキャンされたアイテムが演算子で、topStackアイテムの優先度が高い場合にスタックをポップすることを提案しています。優先順位が高い場合にtopStackアイテムをポップするOR EQANスキャンされたオペレーターと等しいため、最終的に私の計算が正しくなりました。うまくいけば、この問題に取り組んでいる人の助けになります。貴重なリンクを提供してくれたJustin Poliey(およびfas?)に感謝します。
関連する質問 Equation(expression)parser with precedence? には、これを開始する方法に関するいくつかの良い情報があります。
-アダム
「常時接続」アプリケーションを使用している場合は、数学文字列をグーグルに投稿して結果を解析するだけです。簡単な方法ですが、それがあなたが必要とするものかどうかはわかりませんが、私は推測します。
ANTLRは非常に素晴らしいLL(*)パーサージェネレーターです。強くお勧めします。
この質問が聞かれてから11年後の未来:車輪を再発明したくない場合は、エキゾチックな数学パーサーがたくさんあります。
私が数年前に書いたものは、算術演算、方程式の解法、微分計算、積分計算、基本統計、関数/式の定義、グラフ化などをサポートしています。
呼び出される ParserNG およびその無料。
式の評価は次のように簡単です。
_ MathExpression expr = new MathExpression("(34+32)-44/(8+9(3+2))-22");
System.out.println("result: " + expr.solve());
result: 43.16981132075472
_
または、変数を使用して単純な式を計算します。
_ MathExpression expr = new MathExpression("r=3;P=2*pi*r;");
System.out.println("result: " + expr.getValue("P"));
_
または関数を使用して:
_MathExpression expr = new MathExpression("f(x)=39*sin(x^2)+x^3*cos(x);f(3)");
System.out.println("result: " + expr.solve());
result: -10.65717648378352
_
または、与えられた点で導関数を評価するために(シーンの背後でシンボリック微分(数値ではない)を行うため、精度は数値近似の誤差によって制限されません):
_MathExpression expr = new MathExpression("f(x)=x^3*ln(x); diff(f,3,1)");
System.out.println("result: " + expr.solve());
result: 38.66253179403897
_
これは、x = 3でx^3 * ln(x)
を1回区別します。現時点で区別できる回数は1回です。
または数値積分の場合:
_MathExpression expr = new MathExpression("f(x)=2*x; intg(f,1,3)");
System.out.println("result: " + expr.solve());
result: 7.999999999998261... approx: 8
_
このパーサーはかなり高速で、他の多くの機能を備えています。
Objective Cへのバインディングを介してSwiftに移植する作業が完了しました。他の反復的なユースケースの中で、アプリケーションのグラフ化に使用しました。
免責事項:ParserNGは私によって作成されています。
開発者は常にクリーンなアプローチを望み、解析ロジックをゼロから実装しようとします。通常は Dijkstra Shunting-Yard Algorithm で終わります。結果は見栄えの良いコードですが、バグが発生する可能性があります。私はそのようなAPI [〜#〜] jmep [〜#〜] を開発しましたが、それはすべてそれを行いますが、安定したコードを得るには何年もかかりました。
すべての作業が終わったとしても、そのプロジェクトのページからでも、すべての作業が完了した後でも、JavaCCまたはANTLRの使用に切り替えることを真剣に検討していることがわかります。
入力が文字列形式の挿入表現であると仮定すると、それを postfix に変換し、演算子スタックとオペランドスタックのスタックのペアを使用して、そこから解決策を実行できます。 Wikipediaリンクで一般的なアルゴリズム情報を見つけることができます。