前回のプログラムは行の構文解析の部分が想定と違ったので、やはり少し改造は難しいと感じました。そこで構文解析の部分の別のやり方を ChatGPT で作ってもらうことにしました。最後は想定と違うものになりましたが(これはこれで使えそうです)、必要な部分を組み合わせることはできそうです。最近の C++ はよくわからないので、慣れるまでは ChatGPT でやった方が早くできそうです。
C++ で文字列が条件を満たすかどうかを調べる関数を作ってください。関数の戻り値は
&& (条件を満たす文字列が連続している)
|| (どちらかの条件を満たす)
が使えるようにしてください。
と入力すると以下の結果となりました。
C++で、文字列が特定の条件を満たすかどうかを判定する関数を作成し、
その関数の戻り値に対して `&&` や `||` を使えるようにするには、
カスタム型に論理演算子をオーバーロードするのがよい方法です。以下に、条件を満たす文字列の検出・連結・論理演算ができるサンプルを示します。
🔧 サンプルコード
#include <iostream> #include <string> #include <vector> class ConditionResult { public: bool value; std::vector<std::string> matches; ConditionResult(bool v = false) : value(v) {} // 論理積 (AND) ConditionResult operator&&(const ConditionResult& other) const { ConditionResult result(value && other.value); if (result.value) { result.matches = matches; result.matches.insert(result.matches.end(), other.matches.begin(), other.matches.end()); } return result; } // 論理和 (OR) ConditionResult operator||(const ConditionResult& other) const { ConditionResult result(value || other.value); if (result.value) { result.matches = value ? matches : other.matches; if (value && other.value) { result.matches.insert(result.matches.end(), other.matches.begin(), other.matches.end()); } } return result; } // デバッグ用 void print() const { std::cout << "Result: " << (value ? "true" : "false") << "\nMatches:\n"; for (const auto& m : matches) { std::cout << " " << m << "\n"; } } }; // 条件:文字列に特定の単語が含まれているか ConditionResult containsWord(const std::string& input, const std::string& word) { if (input.find(word) != std::string::npos) { return ConditionResult{true, {word}}; } return ConditionResult{false}; } int main() { std::string text = "apple banana cherry"; auto result = containsWord(text, "banana") && containsWord(text, "apple"); result.print(); auto result2 = containsWord(text, "banana") || containsWord(text, "orange"); result2.print(); return 0; }🔍 出力例
Result: true Matches: banana apple Result: true Matches: banana
&& が連続するとき、タプルのような構造としても返されるようにしてください
と入力すると以下の結果となりました。
了解です!
`&&` が連続して使われるときに、条件をすべて満たす場合は、それぞれの結果をまとめた「タプルのような構造」で返したい、ということですね。
つまり:auto result = cond1 && cond2 && cond3;となったときに、`result` には `cond1`〜`cond3` の情報がすべて入っている構造になっていてほしい、ということ。
🧠 方針
✅ 改良版サンプル
#include <iostream> #include <string> #include <vector> class ConditionResult { public: bool value; std::vector<std::string> matches; ConditionResult(bool v = false) : value(v) {} ConditionResult(bool v, const std::vector<std::string>& m) : value(v), matches(m) {} // 論理積 (AND) ConditionResult operator&&(const ConditionResult& other) const { ConditionResult result(value && other.value); if (result.value) { result.matches.reserve(matches.size() + other.matches.size()); result.matches.insert(result.matches.end(), matches.begin(), matches.end()); result.matches.insert(result.matches.end(), other.matches.begin(), other.matches.end()); } return result; } // 論理和 (OR) ConditionResult operator||(const ConditionResult& other) const { ConditionResult result(value || other.value); if (value && other.value) { result.matches = matches; result.matches.insert(result.matches.end(), other.matches.begin(), other.matches.end()); } else if (value) { result.matches = matches; } else if (other.value) { result.matches = other.matches; } return result; } // 出力 void print() const { std::cout << "Result: " << (value ? "true" : "false") << "\nMatches:\n"; for (const auto& m : matches) { std::cout << " " << m << "\n"; } } }; // 条件:指定した単語が含まれているか ConditionResult containsWord(const std::string& input, const std::string& word) { if (input.find(word) != std::string::npos) { return ConditionResult{true, {word}}; } return ConditionResult{false}; } int main() { std::string text = "apple banana cherry"; // AND条件の連鎖(タプル的に記録される) auto result = containsWord(text, "apple") && containsWord(text, "banana") && containsWord(text, "cherry"); result.print(); // OR条件(どれか一つでも) auto result2 = containsWord(text, "grape") || containsWord(text, "banana"); result2.print(); return 0; }🧾 出力例
Result: true Matches: apple banana cherry Result: true Matches: banana
再帰的に構造の中に構造を含むことができるようにして、構文解析に使えるようにしてください
と入力すると以下の結果となりました。
了解です!
再帰的に構造を含む条件評価システムをC++で作って、構文解析にも使えるようにしたいということですね。🔧 方針:構文解析向け 再帰的な条件構造
🧱 基本設計
1. `ConditionNode` という抽象クラスを作成。
2. AND / OR / 単一条件 それぞれを継承クラスで表現。
3. `evaluate()` 関数で再帰的に評価。
4. `matches` の情報も階層的に持たせる。✅ 実装例(再帰的構造)
#include <iostream> #include <memory> #include <string> #include <vector> // 評価結果構造 struct EvalResult { bool value; std::vector<std::string> matches; void print(int indent = 0) const { std::string pad(indent, ' '); std::cout << pad << "Result: " << (value ? "true" : "false") << "\n"; std::cout << pad << "Matches:\n"; for (const auto& m : matches) { std::cout << pad << " " << m << "\n"; } } }; // 抽象クラス class ConditionNode { public: virtual ~ConditionNode() {} virtual EvalResult evaluate(const std::string& input) const = 0; }; // 単一条件ノード class WordCondition : public ConditionNode { std::string word; public: WordCondition(const std::string& w) : word(w) {} EvalResult evaluate(const std::string& input) const override { if (input.find(word) != std::string::npos) { return { true, { word } }; } return { false, {} }; } }; // ANDノード(すべての子が成立) class AndCondition : public ConditionNode { std::vector<std::shared_ptr<ConditionNode>> children; public: void add(const std::shared_ptr<ConditionNode>& cond) { children.push_back(cond); } EvalResult evaluate(const std::string& input) const override { EvalResult result { true, {} }; for (const auto& child : children) { EvalResult sub = child->evaluate(input); if (!sub.value) { result.value = false; } result.matches.insert(result.matches.end(), sub.matches.begin(), sub.matches.end()); } return result; } }; // ORノード(いずれかの子が成立) class OrCondition : public ConditionNode { std::vector<std::shared_ptr<ConditionNode>> children; public: void add(const std::shared_ptr<ConditionNode>& cond) { children.push_back(cond); } EvalResult evaluate(const std::string& input) const override { EvalResult result { false, {} }; for (const auto& child : children) { EvalResult sub = child->evaluate(input); if (sub.value) { result.value = true; result.matches.insert(result.matches.end(), sub.matches.begin(), sub.matches.end()); } } return result; } };🧪 使用例(構文解析風)
int main() { std::string text = "apple banana cherry"; // 条件木を構築 auto root = std::make_shared<AndCondition>(); root->add(std::make_shared<WordCondition>("apple")); auto orNode = std::make_shared<OrCondition>(); orNode->add(std::make_shared<WordCondition>("banana")); orNode->add(std::make_shared<WordCondition>("grape")); root->add(orNode); EvalResult result = root->evaluate(text); result.print(); }🧾 出力例
Result: true Matches: apple banana
これを使って数式の構文解析を行って、その結果を数式を表す構造に変換するプログラムを作ってください
と入力すると以下の結果となりました。
了解です!
この仕組みを使って 数式の構文解析 を行い、結果を 構造(AST: 抽象構文木)に変換するプログラムを作りましょう。🧱 1. ASTノード定義
#include <iostream> #include <memory> #include <string> #include <vector> #include <sstream> #include <cctype> enum class NodeType { Number, BinaryOp }; class ASTNode { public: virtual ~ASTNode() = default; virtual void print(int indent = 0) const = 0; }; using AST = std::shared_ptr<ASTNode>; class NumberNode : public ASTNode { public: double value; NumberNode(double v) : value(v) {} void print(int indent = 0) const override { std::string pad(indent, ' '); std::cout << pad << "Number(" << value << ")\n"; } }; class BinaryOpNode : public ASTNode { public: char op; AST left; AST right; BinaryOpNode(char o, AST l, AST r) : op(o), left(std::move(l)), right(std::move(r)) {} void print(int indent = 0) const override { std::string pad(indent, ' '); std::cout << pad << "BinaryOp('" << op << "')\n"; left->print(indent + 2); right->print(indent + 2); } };🧩 2. トークナイザ
enum class TokenType { Number, Plus, Minus, Mul, Div, LParen, RParen, End }; struct Token { TokenType type; double numberValue = 0; }; class Tokenizer { std::string input; size_t pos = 0; Token current; public: Tokenizer(const std::string& in) : input(in) { next(); } Token peek() const { return current; } void next() { while (pos < input.size() && isspace(input[pos])) ++pos; if (pos >= input.size()) { current = {TokenType::End}; return; } char ch = input[pos]; if (isdigit(ch)) { double val = 0; while (pos < input.size() && isdigit(input[pos])) { val = val * 10 + (input[pos++] - '0'); } current = {TokenType::Number, val}; } else { ++pos; switch (ch) { case '+': current = {TokenType::Plus}; break; case '-': current = {TokenType::Minus}; break; case '*': current = {TokenType::Mul}; break; case '/': current = {TokenType::Div}; break; case '(': current = {TokenType::LParen}; break; case ')': current = {TokenType::RParen}; break; default: throw std::runtime_error(std::string("Unknown character: ") + ch); } } } };🧠 3. 再帰下降パーサ
class Parser { Tokenizer tokenizer; AST parsePrimary() { Token tok = tokenizer.peek(); if (tok.type == TokenType::Number) { tokenizer.next(); return std::make_shared<NumberNode>(tok.numberValue); } else if (tok.type == TokenType::LParen) { tokenizer.next(); // consume '(' AST node = parseExpr(); if (tokenizer.peek().type != TokenType::RParen) throw std::runtime_error("Expected ')'"); tokenizer.next(); // consume ')' return node; } else { throw std::runtime_error("Unexpected token in primary"); } } AST parseTerm() { AST node = parsePrimary(); while (true) { Token tok = tokenizer.peek(); if (tok.type == TokenType::Mul || tok.type == TokenType::Div) { tokenizer.next(); AST right = parsePrimary(); node = std::make_shared<BinaryOpNode>(tok.type == TokenType::Mul ? '*' : '/', node, right); } else { break; } } return node; } AST parseExpr() { AST node = parseTerm(); while (true) { Token tok = tokenizer.peek(); if (tok.type == TokenType::Plus || tok.type == TokenType::Minus) { tokenizer.next(); AST right = parseTerm(); node = std::make_shared<BinaryOpNode>(tok.type == TokenType::Plus ? '+' : '-', node, right); } else { break; } } return node; } public: Parser(const std::string& input) : tokenizer(input) {} AST parse() { AST result = parseExpr(); if (tokenizer.peek().type != TokenType::End) throw std::runtime_error("Unexpected trailing tokens"); return result; } };🧪 使用例
int main() { std::string expr = "1 + 2 * (3 + 4)"; try { Parser parser(expr); AST tree = parser.parse(); std::cout << "AST for: " << expr << "\n"; tree->print(); } catch (const std::exception& ex) { std::cerr << "Parse error: " << ex.what() << "\n"; } return 0; }✅ 出力例
AST for: 1 + 2 * (3 + 4) BinaryOp('+') Number(1) BinaryOp('*') Number(2) BinaryOp('+') Number(3) Number(4)

