逆ポーランド記法を用いた電卓 #C++
- 10 / 18 追記 新しく書き直しました(Python)
こちらの記事 - 私の検索の仕方が悪かったのか、中置記法を後置記号に変換してから、後置記法の計算をするコードが見つからなかったので残しておきます。
目次
| 目次 | テーマ |
|---|---|
| 1 | 前置き |
| 2 | コード |
| 3 | 使い方 |
| 4 | 編集後記 |
| 5 | 参考サイト |
前置き
・C++の初心者が書いたコードなのであまり参考にならないかもしれません。
・もっときれいな書き方やもっとこうした方がいいということがたくさんあると思いますが、私は趣味でプログラミングをしているだけなので、温かい目で見てください。
コード
・以下が実際のコードです。
main.cpp
#include <algorithm>
#include <cctype>
#include <iostream>
#include <string>
#include <vector>
#include <map>
class CppCalculator {
public:
// 実行関数
int run() {
// 式の入力を受け取る
input();
// 数字と演算子以外が含まれていないかの確認
err_check = check_formula();
// エラーチェック
if (err_check == 1) return 1;
// 中置記法から後置記法に変換
parser();
// 後置記法の計算
calculate();
// 結果の表示
result_print();
return 0;
}
private:
// 変数の宣言
std::string formula;
// エラー処理用
int err_check;
// 中置記法の式を保存
std::string normalformula;
// 後置記法の式を保存する
std::vector<std::string> queue;
// 計算結果
double result;
// 式の入力を受け取る
void input(void) {
std::cout << "Formula > ";
std::getline(std::cin, formula);
// 空白の削除
formula.erase(std::remove(formula.begin(), formula.end(), ' '), formula.end());
// 中置記法の式を保存
for (char c : formula) {
normalformula += std::string(1, c) + " ";
}
}
// 数字と演算子以外が含まれていないかの確認
int check_formula(void) {
for (char c : formula) {
if ( !std::isdigit(c) && std::string("+-*/^().").find(c) == std::string::npos ) {
std::cout << "計算できない文字が含まれています\n";
return 1;
}
}
return 0;
}
// 中置記法から後置記法に変換
int parser(void) {
// 数字部分を保存
std::string unitnum = "";
// 演算子の一時的に保存
std::vector<char> stack;
// 演算子の優先順位
std::map<char, int> operatorlist {
{'+', 1},
{'-', 1},
{'*', 2},
{'/', 2},
{'^', 3}
};
// 直前のトークンが数字または演算子だったかを保存する
int negative = 1;
for (char c : formula) {
if (std::isdigit(c) || c == '.') {
unitnum += c;
negative = 0;
} else if (c == '-' && negative == 1) {
unitnum += c;
continue;
}
else if (std::string("+-*/^()").find(c) != std::string::npos) {
if (unitnum != "") {
queue.emplace_back(unitnum);
unitnum = "";
}
if (stack.empty() || stack.back() == '(') {
stack.emplace_back(c);
} else if (c == '(') {
stack.emplace_back(c);
} else if (c == ')') {
while (!stack.empty() && stack.back() != '(') {
queue.emplace_back(std::string(1, stack.back()));
stack.pop_back();
}
// '('の削除
stack.pop_back();
continue;
} else if (operatorlist[stack.back()] < operatorlist[c]) {
stack.emplace_back(c);
} else {
while (!stack.empty() && stack.back() != '(' && operatorlist[stack.back()] >= operatorlist[c]) {
queue.emplace_back(std::string(1, stack.back()));
stack.pop_back();
}
stack.emplace_back(c);
}
negative = 1;
}
}
// 残った数字をキューに代入
if (unitnum != "") queue.emplace_back(unitnum);
// スタックに残った演算子をキューに代入
while (!stack.empty()) {
queue.emplace_back(std::string(1, stack.back()));
stack.pop_back();
}
return 0;
}
void calculate(void) {
// 計算式を一時的に保存
std::vector<std::string> stack;
// 計算用変数
double a, b;
double c = 1.0;
for (std::string s : queue) {
if (std::string("+-*/^()").find(s) != std::string::npos) {
b = stod(stack.back());
stack.pop_back();
a = stod(stack.back());
stack.pop_back();
if (s == "+") stack.emplace_back(std::to_string(a + b));
if (s == "-") stack.emplace_back(std::to_string(a - b));
if (s == "*") stack.emplace_back(std::to_string(a * b));
if (s == "/") stack.emplace_back(std::to_string(a / b));
if (s == "^") {
for (int i = 0; i < b; i++) {
c *= a;
}
stack.emplace_back(std::to_string(c));
c = 1;
}
} else stack.emplace_back(s);
}
result = stod(stack.back());
}
// 結果の出力
void result_print(void) {
std::cout << "中置記法の計算式: " << normalformula << "\n";
std::cout << "後置記法の計算式: ";
for (std::string s : queue) {
std::cout << s << " ";
}
std::cout << "\n";
std::cout << "結果: " << result << "\n";
}
};
int main(void) {
CppCalculator instance;
instance.run();
return 0;
}
・簡単な説明だけすると、入力の受取 → 空白の削除 → 数字または演算子以外が含まれていないかチェック → 中置記法から後置記法へ変換 → 計算 → 結果の表示という流れです。
・後は皆様の方が詳しいと思うのでこれ以上は書かないです。
使い方
・コンパイル
bash
g++ main.cpp -o main
・例
bash
./main
formula > 1 + 2 - ( 3 * 4 ) / 5 + 6 ^ 2 ← ここに入力
中置記法の計算式: 1 + 2 - ( 3 * 4 ) / 5 + 6 ^ 2
後置記法の計算式: 1 2 + 3 4 * 5 / - 6 2 ^ +
結果: 36.6
・使える演算子 + - * / ^ ( )
編集後記
・まだ不完全で計算できない式もある
・解決できれば修正していく
・Pythonで書き始めればよかった...
参考サイト