1. 项目概述与核心价值最近在整理一些关于非经典逻辑的资料发现很多教材和开源库对三元逻辑Ternary Logic的实现要么过于理论化要么就是直接调用现成的库很少看到从底层用C一步步构建一个完整运算系统的分享。正好手头有个小项目需要处理一些具有“不确定”状态的逻辑判断传统的布尔逻辑0和1不够用于是决定自己动手用C实现一个基于二元真值0假1真扩展的三元命题逻辑运算系统。这个系统不仅仅是定义几个新的运算符那么简单它涉及到真值表的重新定义、运算符的重载、表达式的解析与求值乃至一些优化技巧整个过程下来对C的运算符重载、枚举类、函数对象等特性的理解能加深不少。所谓三元命题逻辑就是在传统的“真”True, 1和“假”False, 0之外引入第三个逻辑值通常表示“未知”Unknown或“不确定”Indeterminate。这在数据库的NULL值处理、多值电路设计、模糊逻辑以及某些人工智能的推理场景中非常有用。我们的目标不是创造一个全新的逻辑体系而是基于最熟悉的0和1去模拟和定义这第三个状态比如用2表示未知并构建一套完整的运算规则与、或、非等最终能像计算布尔表达式一样去计算三元逻辑表达式。如果你对C有一定基础对逻辑学感兴趣或者正在寻找一个能综合运用面向对象、运算符重载、解析算法的练手项目那么这个实现过程会非常有收获。接下来我会从设计思路、核心实现、表达式求值到性能优化完整地拆解这个系统的构建过程并分享其中踩过的坑和总结的经验。2. 系统设计与核心思路拆解在动手写代码之前明确设计思路至关重要。一个清晰的设计能避免后期大量的重构。我们的三元逻辑系统核心需要解决几个问题如何表示第三个逻辑值如何定义基本运算如何构建复杂的复合运算以及如何优雅地集成到C中让使用者感觉像在用内置类型一样自然。2.1 真值表示与枚举设计首先我们需要定义三个逻辑值。最直观的想法是使用一个枚举类enum class。为什么不直接用int的0,1,2呢因为enum class提供了更强的类型安全能防止意外的整型隐式转换让代码意图更清晰。enum class TernaryValue : int { FALSE 0, // 假 TRUE 1, // 真 UNKNOWN 2 // 未知 };这里我选择将底层类型指定为int方便后续的一些位操作或转换。UNKNOWN的值设为2这是一个关键选择。在基于二元真值的扩展中我们通常希望“未知”是一个独立于0和1的、离散的第三个值。选择2是因为它在二进制表示上是10与0(00)、1(01)都不同且在进行某些位运算模拟时可以避免混淆。当然你也可以选择-1或其他值但2是一个在正数范围内且与布尔值区分明显的合理选择。2.2 基本运算规则定义真值表这是三元逻辑的核心。对于一元运算如非NOT和二元运算如与AND、或OR我们需要为三个输入值定义所有可能的输出。这构成了一个真值表。以“与”运算AND为例在布尔逻辑中只有两个输入都为真时结果才为真。在三元逻辑中我们需要定义当其中一个或两个输入是UNKNOWN时结果是什么。这里采用一种在数据库和硬件设计中常用的“Kleene逻辑”或“Łukasiewicz逻辑”的变体其核心思想是“未知”具有传染性。即如果运算结果可能因未知值而变得不确定那么结果就是未知。基于这个原则我们可以定义运算规则一元NOT运算¬¬TRUE FALSE,¬FALSE TRUE,¬UNKNOWN UNKNOWN。因为对未知取反结果仍然是未知。二元AND运算∧遵循“有假则假无假有未知则未知全真才真”的原则。FALSE ∧ X FALSE任何值与假相与结果为假TRUE ∧ TRUE TRUETRUE ∧ UNKNOWN UNKNOWN真与未知相与结果取决于未知值故为未知UNKNOWN ∧ UNKNOWN UNKNOWN二元OR运算∨遵循“有真则真无真有未知则未知全假才假”的原则。TRUE ∨ X TRUEFALSE ∨ FALSE FALSEFALSE ∨ UNKNOWN UNKNOWNUNKNOWN ∨ UNKNOWN UNKNOWN我们可以用一个简单的函数来实现这些规则。但更C的做法是重载运算符让TernaryValue类型支持!(NOT),,||,,|等操作。这里需要注意C内置的和||是短路运算符而我们定义的三元逻辑运算通常需要完整求值因为未知值需要参与运算所以更常见的做法是重载非短路的位运算符和|来代表逻辑与和或或者创建新的命名函数。注意直接重载和||运算符对于自定义类型是可行的但由于它们的内置短路行为无法被重载重载后的版本将失去短路特性所有操作数都会被求值。这有时会带来意料之外的开销或副作用因此需要谨慎考虑并在文档中明确说明。在本项目中为了教学清晰和避免混淆我选择重载和|作为逻辑运算符并额外提供and_op、or_op等函数。2.3 系统架构规划一个完整的运算系统不能只有几个孤立的函数。我们需要一个清晰的架构核心类型 (Ternary)封装TernaryValue并重载一系列运算符使其行为像一个内置的逻辑类型。运算函数库除了基本运算符可能还需要实现蕴含Implication、等价Equivalence、异或XOR等衍生运算。表达式求值器能够解析像“(A B) | !C”这样的字符串表达式其中A、B、C是Ternary变量并计算出最终的三元逻辑结果。这需要用到栈和调度场算法。工具与扩展例如将Ternary转换为字符串、从布尔值或整数构造、批量运算的优化等。这样的分层设计使得核心逻辑、运算规则和上层应用解耦便于维护和扩展。3. 核心类实现与运算符重载有了设计蓝图我们开始动手实现最核心的Ternary类。这个类的目标是让使用者可以用近乎自然的方式书写三元逻辑表达式。3.1 Ternary 类的定义与构造首先定义Ternary类它内部封装一个TernaryValue。#include iostream #include string #include stdexcept class Ternary { private: TernaryValue value_; public: // 构造函数 Ternary() : value_(TernaryValue::FALSE) {} // 默认构造为假 explicit Ternary(TernaryValue v) : value_(v) {} explicit Ternary(bool b) : value_(b ? TernaryValue::TRUE : TernaryValue::FALSE) {} // 谨慎提供从int的构造通常只允许0,1,2 explicit Ternary(int i) { switch(i) { case 0: value_ TernaryValue::FALSE; break; case 1: value_ TernaryValue::TRUE; break; case 2: value_ TernaryValue::UNKNOWN; break; default: throw std::invalid_argument(Integer value must be 0, 1, or 2 for Ternary construction.); } } // 获取内部值 TernaryValue value() const { return value_; } // 类型转换谨慎使用 explicit operator bool() const { // 注意这里将UNKNOWN也转换为true通常不合适。 // 更安全的做法是只允许TRUE为真或者抛出异常。 // 这里选择仅当值为TRUE时返回true否则false。 return value_ TernaryValue::TRUE; } // 转换为字符串便于调试和输出 std::string to_string() const { switch(value_) { case TernaryValue::FALSE: return FALSE; case TernaryValue::TRUE: return TRUE; case TernaryValue::UNKNOWN: return UNKNOWN; default: return INVALID; } } // 输出流支持 friend std::ostream operator(std::ostream os, const Ternary t) { os t.to_string(); return os; } };这里有几个关键点explicit关键字用于防止非预期的隐式类型转换。我们不希望int或bool被悄悄地转换成Ternary这容易导致错误。从int构造我们严格限制了输入必须是0、1、2。对于其他值抛出std::invalid_argument异常是更安全的选择。operator bool()这是一个需要特别小心的地方。在C中很多上下文如if条件会尝试进行隐式转换到bool。如果我们定义UNKNOWN转换为true那么if(ternary_unknown)就会成立这很可能不符合逻辑语义。因此这里我采用了最保守的策略只有TRUE才转换为true。你也可以选择不提供这个转换强制用户显式比较。3.2 基本运算符重载实现接下来实现核心的逻辑运算符。我们将重载!非、与、|或。为了清晰我们把运算规则的实现放在独立的静态函数中。class Ternary { // ... 之前的成员 ... public: // 一元逻辑非 (NOT) Ternary operator!() const { switch(value_) { case TernaryValue::FALSE: return Ternary(TernaryValue::TRUE); case TernaryValue::TRUE: return Ternary(TernaryValue::FALSE); case TernaryValue::UNKNOWN: return Ternary(TernaryValue::UNKNOWN); default: return Ternary(TernaryValue::FALSE); // 不应该发生 } } // 逻辑与 (AND) - 使用 运算符 Ternary operator(const Ternary other) const { // 遵循有FALSE则结果为FALSE if (value_ TernaryValue::FALSE || other.value_ TernaryValue::FALSE) { return Ternary(TernaryValue::FALSE); } // 此时两个操作数都不是FALSE。如果其中有UNKNOWN结果为UNKNOWN if (value_ TernaryValue::UNKNOWN || other.value_ TernaryValue::UNKNOWN) { return Ternary(TernaryValue::UNKNOWN); } // 剩下的情况就是两个都是TRUE return Ternary(TernaryValue::TRUE); } // 逻辑或 (OR) - 使用 | 运算符 Ternary operator|(const Ternary other) const { // 遵循有TRUE则结果为TRUE if (value_ TernaryValue::TRUE || other.value_ TernaryValue::TRUE) { return Ternary(TernaryValue::TRUE); } // 此时两个操作数都不是TRUE。如果其中有UNKNOWN结果为UNKNOWN if (value_ TernaryValue::UNKNOWN || other.value_ TernaryValue::UNKNOWN) { return Ternary(TernaryValue::UNKNOWN); } // 剩下的情况就是两个都是FALSE return Ternary(TernaryValue::FALSE); } // 为了方便也提供 和 | 运算符 Ternary operator(const Ternary other) { *this *this other; return *this; } Ternary operator|(const Ternary other) { *this *this | other; return *this; } // 相等与不等比较 bool operator(const Ternary other) const { return value_ other.value_; } bool operator!(const Ternary other) const { return value_ ! other.value_; } };现在我们已经可以像下面这样使用Ternary对象了Ternary t1{TernaryValue::TRUE}; Ternary t2{TernaryValue::UNKNOWN}; Ternary t3{TernaryValue::FALSE}; auto result_and t1 t2; // 结果是 UNKNOWN auto result_or t2 | t3; // 结果是 UNKNOWN auto result_not !t3; // 结果是 TRUE std::cout t1 t2 result_and std::endl;3.3 衍生运算的实现基本的与、或、非构成了一个功能完备集。但为了方便我们通常还会实现一些衍生运算如异或XOR、蕴含Implication, →、等价Equivalence, ↔。它们可以通过基本运算组合而成。class Ternary { // ... 之前的成员 ... public: // 异或 (XOR): A XOR B (A !B) | (!A B) Ternary operator^(const Ternary other) const { return (*this !other) | (!*this other); } // 蕴含 (IMPLIES): A → B !A | B Ternary implies(const Ternary other) const { return !(*this) | other; } // 等价 (EQUIVALENT): A ↔ B (A → B) (B → A) (!A | B) (!B | A) Ternary equivalent(const Ternary other) const { return this-implies(other) other.implies(*this); } Ternary operator^(const Ternary other) { *this *this ^ other; return *this; } };这里展示了如何利用已重载的运算符来构建更复杂的运算。implies和equivalent没有使用运算符重载而是用了命名函数因为-和-在C中不是可重载的运算符这样更清晰。实操心得在重载运算符时保持语义的一致性非常重要。我们重载和|作为逻辑运算符而不是位运算符。这意味着对于Ternary类型和|的行为应该完全由我们定义的真值表决定与它们在整数上的位运算行为无关。在代码注释和文档中必须明确说明这一点避免使用者混淆。4. 表达式解析与求值器实现一个强大的逻辑运算系统应该能处理复杂的表达式字符串例如A (B | !C)。这就需要实现一个简单的表达式求值器。我们将使用经典的调度场算法Shunting Yard Algorithm将中缀表达式转换为后缀表达式逆波兰表示法然后再求值。4.1 词法分析Tokenizer首先我们需要将输入字符串分解成一个个标记Token如变量名、运算符、括号。#include vector #include string #include cctype #include unordered_map enum class TokenType { VARIABLE, // 变量如 A, B, x1 OPERATOR, // 运算符, |, !, ^ PAREN_LEFT, // ( PAREN_RIGHT, // ) END // 结束 }; struct Token { TokenType type; std::string value; // 对于变量存储名称对于运算符存储符号 }; class TernaryTokenizer { public: explicit TernaryTokenizer(const std::string expr) : expression_(expr), pos_(0) {} std::vectorToken tokenize() { std::vectorToken tokens; while (pos_ expression_.length()) { char ch expression_[pos_]; if (std::isspace(static_castunsigned char(ch))) { pos_; // 跳过空白字符 continue; } if (std::isalpha(static_castunsigned char(ch)) || ch _) { // 读取变量名 tokens.push_back(readVariable()); } else if (ch || ch | || ch ! || ch ^) { // 读取运算符 tokens.push_back(Token{TokenType::OPERATOR, std::string(1, ch)}); pos_; } else if (ch () { tokens.push_back(Token{TokenType::PAREN_LEFT, (}); pos_; } else if (ch )) { tokens.push_back(Token{TokenType::PAREN_RIGHT, )}); pos_; } else { throw std::invalid_argument(Invalid character in expression: std::string(1, ch)); } } tokens.push_back(Token{TokenType::END, }); return tokens; } private: std::string expression_; size_t pos_; Token readVariable() { size_t start pos_; while (pos_ expression_.length() (std::isalnum(static_castunsigned char(expression_[pos_])) || expression_[pos_] _)) { pos_; } return Token{TokenType::VARIABLE, expression_.substr(start, pos_ - start)}; } };4.2 调度场算法与求值得到Token流后我们使用调度场算法将其转换为后缀表达式。同时我们需要一个运算符优先级和结合性的映射表。#include stack #include unordered_map class TernaryEvaluator { private: std::unordered_mapstd::string, Ternary variables_; // 存储变量名到Ternary值的映射 // 定义运算符优先级数值越大优先级越高 int getPrecedence(const std::string op) { static const std::unordered_mapstd::string, int prec { {!, 3}, // 非运算优先级最高 {, 2}, {|, 1}, {^, 2}, // 异或通常与优先级相同 }; auto it prec.find(op); if (it ! prec.end()) return it-second; return 0; // 未知运算符 } // 将中缀Token流转换为后缀Token流 std::vectorToken shuntingYard(const std::vectorToken tokens) { std::vectorToken output; std::stackToken opStack; for (const auto token : tokens) { switch(token.type) { case TokenType::VARIABLE: output.push_back(token); break; case TokenType::OPERATOR: { // 当栈顶运算符优先级不低于当前运算符且不是左括号时弹出到输出 while (!opStack.empty() opStack.top().type TokenType::OPERATOR getPrecedence(opStack.top().value) getPrecedence(token.value)) { output.push_back(opStack.top()); opStack.pop(); } opStack.push(token); break; } case TokenType::PAREN_LEFT: opStack.push(token); break; case TokenType::PAREN_RIGHT: // 弹出直到遇到左括号 while (!opStack.empty() opStack.top().type ! TokenType::PAREN_LEFT) { output.push_back(opStack.top()); opStack.pop(); } if (opStack.empty()) { throw std::runtime_error(Mismatched parentheses); } opStack.pop(); // 弹出左括号 break; case TokenType::END: // 处理结束弹出所有剩余运算符 while (!opStack.empty()) { if (opStack.top().type TokenType::PAREN_LEFT) { throw std::runtime_error(Mismatched parentheses); } output.push_back(opStack.top()); opStack.pop(); } output.push_back(token); // 将END也加入作为终止符 break; } } return output; } // 对后缀表达式求值 Ternary evaluateRPN(const std::vectorToken rpnTokens) { std::stackTernary valStack; for (const auto token : rpnTokens) { if (token.type TokenType::VARIABLE) { auto it variables_.find(token.value); if (it variables_.end()) { throw std::runtime_error(Undefined variable: token.value); } valStack.push(it-second); } else if (token.type TokenType::OPERATOR) { if (token.value !) { if (valStack.empty()) throw std::runtime_error(Invalid expression); Ternary op valStack.top(); valStack.pop(); valStack.push(!op); } else { // 二元运算符 if (valStack.size() 2) throw std::runtime_error(Invalid expression); Ternary op2 valStack.top(); valStack.pop(); Ternary op1 valStack.top(); valStack.pop(); if (token.value ) { valStack.push(op1 op2); } else if (token.value |) { valStack.push(op1 | op2); } else if (token.value ^) { valStack.push(op1 ^ op2); } else { throw std::runtime_error(Unsupported operator: token.value); } } } else if (token.type TokenType::END) { break; // 遇到结束符停止处理 } // 括号不应该出现在RPN中 } if (valStack.size() ! 1) { throw std::runtime_error(Invalid expression, evaluation stack error); } return valStack.top(); } public: void setVariable(const std::string name, Ternary value) { variables_[name] value; } Ternary evaluate(const std::string expression) { TernaryTokenizer tokenizer(expression); auto tokens tokenizer.tokenize(); auto rpnTokens shuntingYard(tokens); return evaluateRPN(rpnTokens); } };现在我们可以使用这个求值器了TernaryEvaluator eval; eval.setVariable(A, Ternary(TernaryValue::TRUE)); eval.setVariable(B, Ternary(TernaryValue::UNKNOWN)); eval.setVariable(C, Ternary(TernaryValue::FALSE)); try { Ternary result eval.evaluate(A (B | !C)); std::cout Result: result std::endl; // 输出: Result: UNKNOWN // 因为 B 是 UNKNOWN!C 是 TRUE (B | TRUE) 是 TRUE A TRUE 是 TRUE等等这里需要仔细算。 // 实际计算!C TRUE, (B | TRUE) TRUE, A TRUE TRUE。 // 所以结果应该是 TRUE。让我们验证一下逻辑B是UNKNOWN与TRUE进行OR运算根据规则有真则真结果应为TRUE。 // 那么A(TRUE)与TRUE相与结果为TRUE。所以输出应该是TRUE。 // 这说明我们的求值器逻辑是正确的。 } catch (const std::exception e) { std::cerr Evaluation error: e.what() std::endl; }注意事项在实现表达式求值时运算符的优先级定义至关重要。我们定义了!和^|。这与C/C中逻辑运算符的优先级!^|以及位运算符的优先级有所不同。我们的优先级规则是为我们自定义的三元逻辑运算服务的必须在文档中明确说明。此外调度场算法假设所有二元运算符都是左结合的对于三元逻辑运算这通常是合理的。5. 高级特性与性能优化思考一个基础的系统已经搭建完成。但在实际应用中我们可能还需要考虑更多。5.1 常量折叠与短路求值模拟虽然我们重载的和|不是短路的但在某些性能敏感的场景或者当我们知道某些变量的值比如常量时可以进行优化。例如在表达式FALSE X中无论X是什么结果都是FALSE。我们可以在表达式解析阶段进行简单的常量折叠。更高级的优化可以在求值阶段实现一个“惰性求值”或“短路求值”的版本。例如实现一个lazy_and和lazy_or函数它们接受函数对象作为参数只在需要时才计算第二个操作数。但这超出了基本运算系统的范畴更偏向于特定应用场景的优化。5.2 真值表生成与验证对于逻辑系统生成完整的真值表是验证其正确性的好方法。我们可以编写一个函数遍历所有可能的输入组合计算并输出运算结果。#include iostream #include vector void printTruthTable() { std::vectorTernary values {Ternary(TernaryValue::FALSE), Ternary(TernaryValue::TRUE), Ternary(TernaryValue::UNKNOWN)}; std::cout Truth Table for AND ():\n; std::cout A\\B | FALSE | TRUE | UNKNOWN\n; std::cout -----------------------------\n; for (const auto a : values) { std::cout a.to_string() | ; for (const auto b : values) { std::cout (a b).to_string() | ; } std::cout \n; } std::cout \nTruth Table for OR (|):\n; // ... 类似实现OR }这能直观地检查我们的运算规则是否符合预期比如Kleene逻辑的真值表。5.3 与STL算法的兼容性为了让Ternary类型更易用可以考虑让它与标准库算法兼容。例如我们可以提供std::hashTernary的特化使其能用作std::unordered_map的键。也可以定义比较运算符,等虽然对于三元逻辑来说全序关系可能没有标准定义但有时为了排序容器可以定义一种顺序例如FALSE UNKNOWN TRUE。namespace std { template struct hashTernary { size_t operator()(const Ternary t) const { return hashint()(static_castint(t.value())); } }; } // 定义排序用的比较注意这是人为定义的顺序逻辑上不一定有意义 bool operator(const Ternary lhs, const Ternary rhs) { return static_castint(lhs.value()) static_castint(rhs.value()); }5.4 内存布局与性能考量Ternary类内部只存储一个enum class大小通常就是一个int4字节。在需要存储大量三元逻辑值比如一个大型的真值表或状态数组时可以考虑更紧凑的存储。例如可以用两个比特位来存储一个三态值00FALSE, 01TRUE, 10UNKNOWN, 11保留。这样一个32位整数可以存储16个三元值。但这会大大增加代码的复杂性需要实现专门的位操作函数仅在性能瓶颈确实存在时才值得这样做。对于大多数应用场景当前的封装已经足够高效且易于使用。清晰的接口和正确的语义比极致的微优化更重要。6. 常见问题、调试技巧与扩展方向在实际编码和测试过程中你可能会遇到一些典型问题。这里记录下我踩过的坑和一些调试心得。6.1 运算符重载的陷阱失去短路特性这是我们反复强调的。如果你错误地期望Ternary的和||具有短路特性可能会导致不必要的计算甚至副作用。解决方案是要么明确文档说明要么避免重载它们只使用和|或命名函数。隐式转换的麻烦我们使用了explicit构造函数来避免从int/bool的隐式转换。但operator bool()仍然可能在某些意想不到的上下文如switch语句、算术运算中被调用。一个更安全的做法是不提供operator bool()转而提供isTrue(),isFalse(),isUnknown()等成员函数。bool isTrue() const { return value_ TernaryValue::TRUE; } bool isFalse() const { return value_ TernaryValue::FALSE; } bool isUnknown() const { return value_ TernaryValue::UNKNOWN; }这样判断就需要写成if (t.isTrue())虽然啰嗦一点但意图绝对清晰避免了所有隐式转换的歧义。我强烈推荐在生产代码中使用这种方法。6.2 表达式求值器的健壮性错误处理我们的Tokenizer和Evaluator在遇到非法字符、未定义变量、括号不匹配时会抛出异常。这是正确的做法。在实际应用中你可能需要更丰富的错误信息比如指出错误发生的位置行号、列号。性能对于简单的、一次性的表达式求值调度场算法是足够的。但如果需要反复对同一个表达式但变量值不同求值则每次都要进行词法分析和语法分析效率较低。可以考虑将后缀表达式RPN缓存起来每次求值只执行evaluateRPN部分。支持更多运算符目前只支持了!,,|,^。你可以很容易地扩展它来支持implies比如用-表示和equivalent比如用-表示只需要在Tokenizer、优先级表和求值逻辑中添加相应的处理即可。6.3 测试策略对于逻辑系统充分的测试是保证正确性的关键。应该编写单元测试覆盖所有可能的输入组合。单元测试核心运算对operator!,operator,operator|,operator^等用所有9种输入组合对于二元运算符进行测试验证结果是否符合真值表。测试表达式求值器使用复杂的嵌套表达式进行测试并与手工计算结果对比。测试边界情况如空表达式、单个变量、多个括号等。6.4 扩展方向这个基础的三元逻辑系统可以朝多个方向扩展支持更多逻辑值扩展到四值逻辑True, False, Unknown, Contradiction甚至多值逻辑。核心设计模式是类似的但真值表会呈指数级增长。模糊逻辑三元逻辑可以看作是模糊逻辑的一个特例真、假、中间。可以扩展为使用float在[0,1]区间表示真实度并定义相应的模糊运算如Zadeh算子。集成到更大的系统中例如作为一个专门的库用于处理数据库查询中的三值逻辑SQL中的NULL或者用于数字电路的三态门仿真。提供序列化支持将Ternary对象和表达式序列化为JSON或XML便于存储和传输。6.5 一个完整的简单示例最后让我们看一个将所有这些部分组合在一起的简单示例程序#include iostream #include ternary_logic.h // 假设我们的实现放在这个头文件里 int main() { // 1. 基本运算演示 Ternary t_unknown{TernaryValue::UNKNOWN}; Ternary t_true{true}; // 使用bool构造 Ternary t_false Ternary::FALSE; // 可以定义静态常量这里示意 std::cout !UNKNOWN !t_unknown std::endl; std::cout TRUE UNKNOWN (t_true t_unknown) std::endl; std::cout FALSE | UNKNOWN (t_false | t_unknown) std::endl; std::cout TRUE ^ UNKNOWN (t_true ^ t_unknown) std::endl; // 2. 表达式求值演示 TernaryEvaluator evaluator; evaluator.setVariable(P, Ternary(TernaryValue::TRUE)); evaluator.setVariable(Q, Ternary(TernaryValue::UNKNOWN)); evaluator.setVariable(R, Ternary(TernaryValue::FALSE)); std::string expr !P (Q | R); try { Ternary result evaluator.evaluate(expr); std::cout \nExpression: expr std::endl; std::cout PTRUE, QUNKNOWN, RFALSE std::endl; std::cout Result: result std::endl; // 应输出 FALSE // 计算!P FALSE, (Q|R)UNKNOWN, FALSE UNKNOWN FALSE } catch (const std::exception e) { std::cerr Error: e.what() std::endl; } // 3. 真值表生成 std::cout \n--- Generated Truth Table Snippet ---\n; printTruthTable(); // 调用之前定义的函数 return 0; }实现这个系统的过程本质上是一次对C语言特性枚举类、运算符重载、类设计和经典算法调度场算法的深入实践。它不仅仅是一个逻辑运算工具更是一个如何设计一个自包含、接口清晰、易于扩展的C小型库的范例。
网站建设
高端定制
企业官网