(附前文)习惯上我们在应用系统中一直使用两值逻辑:非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;
}
}
}
}