(附前文)习惯上我们在应用系统中一直使用两值逻辑:非True即False。两值逻辑的运算体系已经相当成熟,与、或、非以及衍生的异或、与非等等。但是在实际应用中,我们会有机会遇到三值逻辑。 三值逻辑通常包含可选的True、False、NULL。如何在完备的两值逻辑运算体系中加入这个NULL,使之满足我们的需要,并且不会引发逻辑矛盾,就是我们要在这里讨论的。 NULL参与逻辑运算时,实际上存在着不同的算法。按NULL值在运算中的“优先级”分为三种。 通常我们在数据库中使用的三值逻辑,遵循NULL最优的原则。有NULL值参与二值运算时,返回结果为NULL,其它与二值逻辑相同。这一原则基于关系型数据库将NULL视为“无意义(Nil)”。由于其内容无意义,则任何逻辑值与之进行运算的结果都是无意义值,这也与许多数据关系型数据库对NULL的处理一致。 另一方面,可能有些朋友没有注意到,事实上常见的权限体系也是一种三值逻辑,这个体系里NULL值处于最低优先级,两个权限值进行合并时,False(否定)高于一切,没有否定值时,肯定值(True)高于NULL,只有两个值均为NULL时,结果才为NULL。实际上我们可以将它看作是一个三值与运算,那么出于数学上的对称,很容易可以构造出对应的逻辑或运算。这种运算规则视NULL为“未赋值(Undefine)”。 最后一种规定NULL值的优先级介于True和False之间,与运算,二者有一为False时,返回False,否则二者有一为NULL时,返回NULL,二者均为True时返回True;或运算,二者有一为True时返回True,否则二者有一为NULL时返回NULL,二者均为False时返回False。这等于分别承认True和False在或和与运算中的最高优先级。它基于NULL值为“未知”的理念。这种规则下,对NULL值严格视为未知的True或False(Unknown)。也有一些数据库的三值逻辑是基于这个体系的,MSDN中给出了一个基于此规则的DBBool示例。 关于NULL值的学术讨论其实一直没有休止过,这期间还出现过四元逻辑等理论。实践也证明,“真实”世界中客观存在着不止一种三值逻辑体系。在实用中应当依据具体情况选择最适合的规则。 我不想隐瞒,写下这一章的时候,我心里很自豪。对于真正的高手,那些创造了强大的虚拟机、框架和优雅类库的大师来讲,MultiBoolean这样的小东西确实不算什么。但是,这确是第一个可以让我自己满意的作品。虽然这个设计还不能作到完美,但是它已经可以满足我当前对多值逻辑的应用需求。 长期以来,困扰我的最大问题莫过于多值逻辑中不同的空值如何兼容。当然,从数学意义来讲,不同的空值代表了不同的逻辑空间,它们之间不应该存在交叉。所以理想状态应该是根据某种标记来区分不同的逻辑,并将它们分隔开。但是在实用中,一个可以在不影响各自的逻辑算法、不引发异常的前提下处理多种空值逻辑的多值逻辑类型,似乎更为完美。毕竟它是一个基础类型,应该使用尽可能简单——我的Blog上记载了另一种多值逻辑实现,在实用中,我感觉它确实过于复杂了。一个只有几种状态的逻辑类型,居然需要工厂方法来构造…… 引起这种复杂结构的直接原因,即是我一直试图用-1,0,1三个整型来表达逻辑三态。也许对于某一种逻辑来讲,这样作是合适的,但是如果想要用一个0来表达多个逻辑空值,未免太过苛求了。 用多个整数来表达?这种方式我也想过,比如将Nil设为最小值,UnKnown设为最大值。然而很难实现一个简单优雅的算法来兼容这些值,本来UnKnown本身的算法很简洁漂亮,而其它另两种虽然不够直观,但是足以内洽。当把它们放在一起的时候,不应该产生任何冲突。 在一个偶然的机会,我想到了复数。是的,复数正是我需要的表达形式!如果将有效逻辑值表达为实数,将空值表达为虚数,这正是我所需要的效果。在形式上,由于Unknown的含义,恰恰可以将它视为“真正的空值”或“可能的逻辑值”两种体系的一个交叉点,也就是说,它正是那个实虚轴交叉的原点——0。而这也与MSDN中的空值算法兼容。 这是一个令我激动的发现。很快,我着手寻找合适的复数实现。由于.net CLR中没有提供内置的复数类型,我参考STL中的复数实现编写了一个C#版本的Complex类型。后面的事,就是怎样把这个想法具体化了。 以下就是实现代码,在代码的文档注释中,有各个主要功能的详细介绍: using System; using System.Data; using March.Math;
namespace March.VBoolean { /// <summary> /// MultiBoolean 是 VBoolean命名空间中最后加入的一个成 /// 员,从结构来讲,可能也是多值离散逻辑的最终类型了。相比 /// 同一空间内的VarBoolean和Boolw3,它在数学上统一了三种空 /// 值规则,成为一个完备实用的数据类型。后两者今后将只作为 /// 向下兼容而存在,并且在以后的版本中可能会去掉这两个类型。 /// </summary> public struct MultiBoolean { private Complex __value; private static readonly Complex TrueValue = new Complex(1, 0); private static readonly Complex FalseValue = new Complex(-1, 0); private static readonly Complex UnknownValue = new Complex(0, 0); private static readonly Complex UndefineValue = new Complex(0, 1); private static readonly Complex NilValue = new Complex(0, -1); /// <summary> /// 真值 /// </summary> public static readonly MultiBoolean True = new MultiBoolean(new Complex(1, 0));
/// <summary> /// 假值 /// </summary> public static readonly MultiBoolean False = new MultiBoolean(new Complex(-1, 0));
/// <summary> /// MultiBoolean 支持的空值类型之一,表示该值是真或假中 /// 的一种,但是不能确定。 /// </summary> public static readonly MultiBoolean Unknown = new MultiBoolean(new Complex(0, 0));
/// <summary> /// MultiBoolean 支持的空值类型之一,表示未定义的逻辑值。 /// 如果限定逻辑状态为True,False,Undefine中的一种,那么与运算即为权限管 /// 理体系中的权限校验操作。 /// </summary> public static readonly MultiBoolean Undefine = new MultiBoolean(new Complex(0, 1)); /// <summary> /// MultiBoolean 支持的空值类型之一,表示无意义的逻辑值。 /// </summary> public static readonly MultiBoolean Nil = new MultiBoolean(new Complex(0, -1));
/// <summary> /// MultiBoolean 内部以复数表示逻辑状态,通过这个内部的 /// 构造函数生成指定状态的逻辑值。 /// </summary> /// <param name="value">代表状态的复数值,可能值为1,-1,i,-i,0</param> private MultiBoolean(Complex value) { this.__value = value; }
public bool IsTrue{get{return __value == TrueValue;}} public bool IsFalse{get{return __value == FalseValue;}} public bool IsUnknown{get{return __value == UnknownValue;}} public bool IsUndefine{get{return __value == UndefineValue;}} public bool IsNil{get{return __value == NilValue;}}
/// <summary> /// 两值逻辑向 MultiBoolean 转型的隐式转型函数。 /// 因为 MultiBoolean 中定义了多种可选的空值,CLR 的 DBNull并未定义 /// 与二值逻辑的转型规则,所以在这里不定义DBNull向 MultiBoolean 的隐式 /// 转换。需要进行转换的用户可以根据自己的应用逻辑定义自己的类型转换。 /// </summary> /// <param name="x">逻辑值</param> /// <returns>转换后的 MultiBoolean </returns> public static implicit operator MultiBoolean(bool x) { return x? True: False; }
/// <summary> /// MultiBoolean的与操作。 /// 返回值按Nil, False,Unknown,True, UnDefine 的优先级从两值中取一返回。 /// 如果限定逻辑状态为True,False,Undefine中的一种,那么该与运算即为权限管 /// 理体系中的权限校验操作。 /// </summary> /// <param name="x">左值</param> /// <param name="y">右值</param> /// <returns>返回值</returns> public static MultiBoolean operator & (MultiBoolean x, MultiBoolean y) { if(x.IsNil || y.IsNil) return Nil;
if(x.IsUndefine ) return y; if(y.IsUndefine) return x;
return new MultiBoolean(x.__value.real < y.__value.real ? x.__value : y.__value); }
/// <summary> /// MultiBoolean的或操作。 /// 返回值按Nil, True,Unknown,False, UnDefine 的优先级从两值中取一返回。 /// </summary> /// <param name="x">左值</param> /// <param name="y">右值</param> /// <returns>返回值</returns> public static MultiBoolean operator | (MultiBoolean x, MultiBoolean y) { if(x.IsNil || y.IsNil) return Nil;
if(x.IsUndefine) return new MultiBoolean(y.__value); if(y.IsUndefine) return new MultiBoolean(x.__value);
return new MultiBoolean(x.__value.real > y.__value.real ? x.__value : y.__value); }
/// <summary> /// MultiBoolean的非操作。True和False互为非值。 /// 空值的非操作通常没有意义,仍返回空值本身。当且仅当 /// x为True或False时,!x=x&False(实际上x==Nil时也满足这个 /// 规则,但其行为与T/F值的非不相同)。 /// </summary> /// <param name="x">操作数</param> /// <returns>返回值</returns> public static MultiBoolean operator ! (MultiBoolean x) { return new MultiBoolean(new Complex(-x.__value.real, x.__value.imag)); }
/// <summary> /// MultiBoolean 对象的等值判断,需要指出的是,对于空 /// 值,判断其是否相等没有意义,故两值中有至少一个为空时, /// 按以下规则判断: /// 只有一个为空值时,返回该空值状态; /// 两个都为空值时,返回优先级较高的一个。 /// 定义这样的规则只是为了方便最常见的三值逻辑应用,我认为不应该出现不同空值 /// 之间发生比较的场合,至少这样的运算非常罕见,而它的数学意义也模糊不清。 /// 需要定义自己的判等规则时,可以调用对象的实例方法Equals或静态方法Equals。 /// </summary> /// <param name="x">左值</param> /// <param name="y">右值</param> /// <returns>返回值</returns> public static MultiBoolean operator == (MultiBoolean x, MultiBoolean y) { if(x.__value.real == 0 && y.__value.real == 0) return new MultiBoolean(new Complex(0, System.Math.Min(x.__value.imag, y.__value.imag)));
if(x.__value.real == 0 && y.__value.real != 0) return new MultiBoolean(x.__value);
if(x.__value.real != 0 && y.__value.real == 0) return new MultiBoolean(y.__value);
return x.__value.real == y.__value.real ? True : False; }
/// <summary> /// MultiBoolean 对象的不等判断,需要指出的是,对于空 /// 值,判断其是否相等没有意义,故两值中有至少一个为空时, /// 按以下规则判断: /// 只有一个为空值时,返回该空值状态; /// 两个都为空值时,返回优先级较高的一个。 /// 定义这样的规则只是为了方便最常见的三值逻辑应用,我认为不应该出现不同空值 /// 之间发生比较的场合,至少这样的运算非常罕见,而它的数学意义也模糊不清。 /// 需要定义自己的判等规则时,可以调用对象的实例方法Equals或静态方法Equals。 /// </summary> /// <param name="x">左值</param> /// <param name="y">右值</param> /// <returns>返回值</returns> public static MultiBoolean operator != (MultiBoolean x, MultiBoolean y) { if(x.__value.real == 0 && y.__value.real == 0) return new MultiBoolean(new Complex(0, System.Math.Min(x.__value.imag, y.__value.imag)));
if(x.__value.real == 0 && y.__value.real != 0) return new MultiBoolean(x.__value);
if(x.__value.real != 0 && y.__value.real == 0) return new MultiBoolean(y.__value);
return x.__value.real == y.__value.real ? False : True; }
/// <summary> /// 异或运算,这里按x^y=(!x&y)|(x&!y)规则,当且仅当x和y为True/False中一个 /// 时,表达式与!=行为相同。 /// </summary> /// <param name="x">左值</param> /// <param name="y">右值</param> /// <returns>返回值</returns> public static MultiBoolean operator ^ (MultiBoolean x, MultiBoolean y) { return (!x&y)|(x&!y); }
public static bool operator true(MultiBoolean x) { return x.__value.real == 1; }
public static bool operator false(MultiBoolean x) { return x.__value.real == -1; }
/// <summary> /// MultiBoolean 对象的等值状态判断,需要指出的是,对于空 /// 值,判断其是否相等没有意义,这里只是判断两个对象是否为同一 /// “状态”,而它们在数学意义上不一定“相等”。 /// 这个方法是重载System.Object.Equals(System.Object obj) /// 方法的通用接口。 /// </summary> /// <param name="x">操作数</param> /// <returns>返回值</returns> public override bool Equals(object obj) { if(obj.GetType() != this.GetType()) return false;
return this.Equals((MultiBoolean)obj); }
/// <summary> /// MultiBoolean 对象的等值状态判断,需要指出的是,对于空 /// 值,判断其是否相等没有意义,这里只是判断两个对象是否为同一 /// “状态”,而它们在数学意义上不一定“相等”。 /// </summary> /// <param name="x">操作数</param> /// <returns>返回值</returns> public bool Equals(MultiBoolean x) { return this.__value == x.__value; }
/// <summary> /// MultiBoolean 对象的等值状态判断,需要指出的是,对于空 /// 值,判断其是否相等没有意义,这里只是判断两个对象是否为同一 /// “状态”,而它们在数学意义上不一定“相等”。这个静态方法是 /// 为了用户自定义逻辑体系而预留的,它与==运算符的判等规则不同。 /// </summary> /// <param name="x">左值</param> /// <param name="y">右值</param> /// <returns>返回值</returns> public static bool Equals(MultiBoolean x, MultiBoolean y) { return x.__value == y.__value; }
/// <summary> /// 获取Hash值,已重载 /// </summary> /// <returns>对象的Hash值与它的值相对应。</returns> public override int GetHashCode() { return this.__value.GetHashCode (); }
/// <summary> /// 将MultiBoolean对象表达为对应的字符串。 /// </summary> /// <returns>与对象对应的字符串</returns> public override string ToString() { if(this.__value == TrueValue) return "True"; if(this.__value == FalseValue) return "False"; if(this.__value == UndefineValue) return "Undefine"; if(this.__value == NilValue) return "Nil"; return "Unknown"; }
/// <summary> /// 将字符串解析为对应的MultiBoolean值。 /// </summary> /// <param name="input">待解析的字符串值,可选的范围是"True", "False", "Unknown", "Undefine, "Nil"。</param> /// <returns>对应的MultiBoolean值。</returns> public static MultiBoolean Parse(string input) { switch(input) { case "True": return True; case "False": return False; case "Unknown": return Unknown; case "Undefine": return Undefine; case "Nil": return Nil; default: throw new ArgumentException("The string isn't a available Value.", input); } }
/// <summary> /// 从数据库字段值中取出逻辑值,并将取转换为MultiBoolean,DBNull对应的值取决于 /// 用户传入的IsNull变量 /// </summary> /// <param name="DBValue">数据库接口的GetValue方法返回的通常是一个object,可能的值为True,False,DBNull</param> /// <param name="IsNull">DBNull对应的值</param> /// <returns>返回的MultiBoolean</returns> public static MultiBoolean ReadDBValue(object DBValue, MultiBoolean IsNull) { if(DBValue is bool) return (bool)DBValue ? True : False; if(DBValue is DBNull) return IsNull; throw new ArgumentException("输入参数不是有效的逻辑值", "DBValue"); }
/// <summary> /// 将当前对象封装为T/F/DBNull的 /// 三值对象,用于对数据库访问接口赋 /// 值,这个过程中会损失空值状态,所 /// 以它与ReadDBValue并非互逆操作。 /// </summary> /// <returns>封装好的对象</returns> public object ToDBOjbect() { switch(Convert.ToInt32(this.__value.real)) { case 1 : return (object)true; case -1 : return (object)false; default: return DBNull.Value; } } } }

|