計算式ライブラリを作ってみた。

かなり昔に計算式をUIで設定したり設定ファイル等に書いておいて実行できると便利かもと思って何度か作ってみたことがある。基本的なアルゴリズムというのは自分の好みにあってるらしく興味深いものを感じるし、例えば...計算式自体を変更しなければならなくなったときプログラムを修正することなく設定変更だけで対応することができたりしたら便利なはずだ。

Shunting Yard Algorithmについて
Shunting-yard algorithm

あまりに久しぶりすぎて全て忘れてる状態だったので上記を参考にさせてもらったがとても分かりやすくまとめられている。

今回のはShunting-yard algorithm に、関数(可変長引数&引数省略対応)、シンボル参照、構文エラーチャック、結果が不変な式の最適化などを追加してみたが、このアルゴリズムはわかりやすくシンプルでとても良い。これ一択でいいんじゃね?と思ってしまった。

演算子は、C言語の代入演算子、配列演算子、ドット演算子、三項演算子を除く演算子に対応。三項演算子は実装を複雑にしてまで対応するメリットがないというかそもそもIF()関数を実装すれば必要ないものなので三項演算子はあえて非対応としてみた。

おまけの関数はエクセルを参考に汎用的に使えそうなものを実装してみたがメリットがあるのかどうかは不明。

[単項演算子]

[二項演算子]

[比較演算子]

[論理演算子]

[括弧演算子]

[関数]

[シンボル参照]

[データ型]

データ型は必要とされる型に自動変換するので型についてはあまり気にしなくても良い。例えば、1 + 2 と “1” + “2” の結果は同じく 3 である。文字型から数値型への変換は問題ないが、小数桁を含む数値型を文字型に変換した場合に精度落ちすることがあるので注意すること。ブランク型を変換した場合はゼロ(0)或いは空文字(“”)として扱われる。

C++17以降に対応したコンパイラとコンパイラ・オプション(-std=c++17)が必要となることと、時刻系の関数の実装にlocaltime_r()を使ってるのが原因でインクルードの順番によってlocatime_rがエラーになる場合がある。もし、localtime_rがエラーになったら、expr.hを一番最初にインクルードしてみるべし。
もしくは、-d_POSIX_THREAD_SAFE_FUNCTIONSをコンパイラ・オプションに追加しても良い。

【修正】
2024-11-06
比較演算子の文字列対応と細かいバグ修正を行った。
それとcall()関数を試験的に追加。call(“1 + 2 * 3”)のように他の計算式の実行結果を参照することができる。
expr1: “1 + 2 * 3”
call(expr1)+3=10
計算式に名前を付けておくとサブルーチンのような使い方ができそう。まだバグッてるがとりあえず公開。
ついでにサンプルプログラムに実行時間を計測する機能を追加してみたら...1億回回しても100ms程度しかかからない。ということは1回当たり1ns程度となるが何か間違ってるのかな...

【サンプル・プログラム (jsonパーサーとの組み合わせ)】

【ライブラリ】