65.9K
CodeProject 正在变化。 阅读更多。
Home

SSTypesLT 库。三元逻辑类型 SmartBool 的实现

emptyStarIconemptyStarIconemptyStarIconemptyStarIconemptyStarIcon

0/5 (0投票)

2019年4月20日

MIT

8分钟阅读

viewsIcon

8426

downloadIcon

129

使用 C# 编程语言实现的三态(三进制)逻辑类型

在编译和运行之前,请阅读代码注释或下方 "使用代码" 一章中关于如何安装必要库的说明。

引言

编程语言中的标准布尔类型只有“true”和“false”两种状态。在某些情况下,这还不够。本文简要介绍了 SSTypesLT 库中实现的三元(三状态)布尔类型的开发和实际使用。

动机

三元逻辑是一种除了“True”和“False”之外,还有第三个值(例如称为“Undefined”或“Unknown”)的逻辑。现代编程语言仅直接支持双状态(二进制)布尔逻辑。这是硬件特性的结果,因为硬件是基于二进制系统运行的。但在某些情况下,三元逻辑对程序员非常有帮助。

数据库管理系统为所有表示数据缺失的数据类型都支持 NULL 值。该值与逻辑上的“Unknown”具有相同的语义——缺失数据或未知值。作者在配置文件中也遇到了同样的问题,即获取布尔键的缺失值信息并不容易。实际上,三元逻辑扩展了其他数据处理技术的可能性。例如,比较两个 double 值,如果其中一个值为 NAN,则可以用三元逻辑实现,结果为“Unknown”。

SSTypesLT 库

本文介绍了 SSTypesLT 库中的 SmartBool 类型,该类型计划在项目 www.GoMap.Az 的数据处理和质量控制中得到广泛应用。作者希望有时间开发 SSTypesLT 库并对其进行描述。作者衷心邀请读者尝试 SSTypesLT 库并提供反馈。

SSTypesLT 库是一个开源项目,位于 此处SSTypesLT 库是专有的 SSTypes 库的一个开放部分,后者在项目 www.GoMap.Az 中得到了广泛应用。作者感谢

C# 中的可空布尔类型

可空布尔类型可用于在 C# 中实现三元逻辑。语义上,“null”值对于可空布尔变量完全等同于数据库中的 NULL 值。但这并非旨在支持第三种逻辑状态,因为它是由泛型提供的,泛型适用于任何值类型。可空布尔是这种语言特性在实现三元逻辑中的应用。

因此,这种方法存在一些缺点:

  • null”值与“true”和“false”相比,具有不同的语义和行为。
  • 与可空布尔变量进行操作时,如果值为“null”,可能会抛出异常。而“true”和“false”的操作则永远不会抛出异常。
  • 在某些操作中,可空布尔变量(例如在条件语句中使用)需要显式转换为 bool 类型。
  • 可空布尔变量需要额外的内存空间来存储“hasValue”标志(参见 struct Nullable <T>, Microsoft Reference Source)。

以下代码片段展示了此类缺点的示例。

bool? bn = null;
bool b2 = (bool)bn;

此代码显示了以下缺点:

  • 将变量 bn 的值赋给变量 b2 时会抛出异常。
  • 将变量 bn 的值赋给变量 b2 时需要显式转换为 bool 类型(这不能算作缺陷,因为可空布尔比 bool 更具信息量)。
  • 将“null”值(而非“unknown”或“Undefined”)赋给变量 bn(语义问题)。
bool? bn = null;

if ((bool)bn)
{
    Console.WriteLine("True");
}
else
{
    Console.WriteLine("False or Unknown");
} 

此代码显示了以下缺点:

  • if”语句会抛出异常。
  • if”语句需要显式转换为 bool 类型。
  • 如果 bnnull,“False or Unknown”字符串永远不会显示(语义问题)。
  • 将“null”值(而非“unknown”或“undefined”)赋给变量 bn(语义问题)。

运算符 "&&" 和 "||"

可空布尔未实现“&&”和“||”运算符。以下代码将无法编译,因为“&&”运算符不能应用于可空布尔。

bool? nb1 = null;
bool? nb2 = true;

bool? nbt1 = nb1 && nb2;
bool? nbt2 = nb1 & nb2;

可空布尔类型的扩展

对可空布尔类型的扩展可以提高代码的可读性、语义和行为(参见 Ternary logic.)。但这并不能解决将可空布尔类型用于实现三元逻辑的所有问题。

C# 中的 SmartBool 类型

SmartBoolSSTypeLT 库中的一个结构(参见 SSTypes 项目。)。该结构具有允许以对程序员最方便的方式实现三元逻辑的特性。另一个结构 SmartInt(表示 32 位整数)也采用了类似的方法开发,并且在网站 www.GoMap.Az 中成功使用了数年(参见 Accelerated .NET Types.)。

SmartBool 基于 sbyte(有符号字节)类型。这种方法允许在不使用跳转语句(“if”语句)的情况下实现否定运算符“!”。跳转会降低 CPU 流水线的可预测性,影响性能。不幸的是,在二进制硬件平台上,几乎所有三元逻辑操作都无法通过单个指令实现。SmartBool 的开发目标是内存小且在“if”语句中速度快。SmartBool 和可空布尔的性能已进行了基准测试和比较(参见下面的“性能”一章)。

SmartBool 的行为与可空布尔的行为略有不同。以下代码在逻辑上与上面提供的代码类似,但行为有所不同。

SmartBool sb = SmartBool.Unknown;
bool sb2 = (bool)sb;

if (sb)
{
    Console.WriteLine("True");
}
else
{
    Console.WriteLine("False or Unknown");
}

代码的优点是:

  • 变量 sb 的值为 SmartBool.Unknown,这在语义上是逻辑值,而不是缺失值的表示。
  • sb 的值赋给 sb2 时不会抛出异常。
  • if”语句语义正确,不会抛出异常。

第二行中的隐式类型转换是必需的,因为 SmartBoolbool 更具信息量。

“true”和“false”运算符

Microsoft 的 C# 参考文档描述了“true”和“false”运算符(参见 true and false operators (C# Reference).),如下所述。具有已定义的 true 运算符的类型可以是“if”、“do”、“while”和“for”语句以及条件运算符“?:”中控制条件表达式的结果类型。

true”运算符返回布尔值 true,表示操作数确定为 true。“false”运算符返回布尔值 true,表示操作数确定为 false。“true”和“false”运算符不保证互补。也就是说,对于同一个操作数,“true”和“false”运算符都可能返回布尔值 false

SmartBool 类型根据此描述实现了“true”和“false”运算符。这意味着以下代码将按注释中所述运行。

SmartBool sb1 = ...
SmartBool sb2 = ...
...

if (sb1)
{
    // Run if sb1 is True
    Console.WriteLine("True");
}
else
{
    // Run if sb1 is False or Unknown
    Console.WriteLine("False or Unknown");
}

if (!sb2)
{
    // Run if sb2 is False
    Console.WriteLine("False");
}
else
{
    // Run if sb2 is True or Unknown
    Console.WriteLine("True or Unknown");
}

因此,对于上述两种情况,对于 Unknown 值,将执行“else”部分。

运算符 "&&" 和 "||"

SmartBool 实现运算符“&&”和“||”。以下代码将毫无问题地编译。

SmartBool sb1 = null;
SmartBool sb2 = true;

SmartBool sbt1 = sb1 && sb2;
SmartBool sbt2 = sb1 & sb2;

相等运算符“==”和“!=”

SmartBool 的相等运算符“==”和“!=”返回 SmartBool 类型的结果。而 bool 和可空 Bool 的这些运算符返回 bool 类型的结果。这是 SmartBoolbool 和可空 Bool 的重要区别。

将未知值与已知值进行比较的语义可以描述为结果未知的过程。对于长时间计算,如果结果依赖于此输入值,那么输入中的未知值应产生未知结果。SmartBool 支持此语义,但可空布尔不支持。

以下代码展示了这种区别。

bool? nb1 = null;
bool? nb2 = true;

bool? nbt1x = nb1 == nb2;
// nbt1x has false value here

SmartBool sb1 = null;
SmartBool sb2 = true;

SmartBool sbt1x = sb1 == sb2;
// sbt1x has SmartBool.Unknown value here

SmartBool 的真值表

SmartBool 支持三元逻辑的标准真值表。三元逻辑的真值表在许多来源中都有呈现。

NOT 运算(运算符“!”)

OR 运算(运算符“|”和“||”)

AND 运算(运算符“&”和“&&”)

相等运算(运算符“==”)

蕴含运算

异或运算(运算符“^”)

性能

基准测试表明,对于运算符“!”、“true”和“false”,SmartBool 的性能优于可空布尔。SmartBool 的 OR 运算符“|”和 AND 运算符“&”的基准测试显示,与可空布尔相比,性能稍慢。

与可空布尔相比,SmartBool 的相等运算符性能不佳。但这是因为 SmartBool 的相等运算符实现了一个稍微复杂的逻辑并返回 SmartBool。而可空 Bool 的相等运算符返回 bool。请参见上面“SmartBool 类型在 C# 中 / 相等运算符“==”和“!=””一章。

下表显示了这三种类型的几个操作的基准测试。这些值是相对的,仅用于比较。运算符“true”和“false”的值乘以 1000。该表基于下方表格中的数据构建。

下表展示了赋值运算符的基准测试。如表中所示,将 bool 赋值给 SmartBool 的性能相当低。这是由于运算符逻辑中的几个“if”检查。同时,将 SmartBool 赋值给 SmartBool 的速度比可空 Bool 赋值给可空 Bool 要快一些。

原始基准测试输出表。请参阅下方“使用代码”章节中链接提供的可下载 zip 压缩包中的代码。

                       Method |      Median |      StdDev |
----------------------------- |------------ |------------ |
        Asg_Bool2NullableBool |  32.1589 us |  14.2492 us |
           Asg_Bool2SmartBool | 137.1827 us |  64.1514 us |
                Asg_Bool2Bool |  23.0328 us |   9.5130 us |
Asg_NullableBool2NullableBool |  39.7319 us |  15.7200 us |
      Asg_SmartBool2SmartBool |  28.5209 us |  11.4954 us |
                NOT_Bool2Bool |  31.4698 us |   8.9368 us |
NOT_NullableBool2NullableBool | 111.9670 us |  38.0665 us |
      NOT_SmartBool2SmartBool |  28.3513 us |   9.2716 us |
                 OR_Bool2Bool |  38.7323 us |  17.7660 us |
 OR_NullableBool2NullableBool | 204.6962 us |  76.5525 us |
       OR_SmartBool2SmartBool | 254.5754 us | 102.0478 us |
                AND_Bool2Bool |  37.4744 us |  11.4081 us |
AND_NullableBool2NullableBool | 170.3016 us |  54.8486 us |
      AND_SmartBool2SmartBool | 194.5647 us |  60.3215 us |
                 EQ_Bool2Bool |  35.4095 us |  12.3996 us |
 EQ_NullableBool2NullableBool |  72.4243 us |  22.2527 us |
       EQ_SmartBool2SmartBool | 184.6553 us |  56.8425 us |
                NEQ_Bool2Bool |  37.7590 us |  12.0692 us |
NEQ_NullableBool2NullableBool |  70.6348 us |  21.5845 us |
      NEQ_SmartBool2SmartBool | 154.4584 us |  62.6584 us |
                    TRUE_Bool | 317.5711 ns |  82.5786 ns |
            TRUE_NullableBool | 425.9031 ns | 140.4248 ns |
               TRUE_SmartBool | 299.9301 ns |  99.3415 ns |
                   FALSE_Bool | 312.6404 ns | 102.9978 ns |
           FALSE_NullableBool | 494.9714 ns | 133.1724 ns |
              FALSE_SmartBool | 294.6255 ns |  67.9278 ns |

测试环境

BenchmarkDotNet =v0.9.7.0
OS=Microsoft Windows NT 6.2.9200.0
Processor=Intel(R) Core(TM) i7-3610QM CPU 2.30GHz, ProcessorCount=8

注意:由于散热问题,CPU 频率被强制设置为 1.2 GHz。

摘要

为了简洁起见,可以总结如下结果:

  • 可空 Bool (bool?) 的设计初衷并非用于实现三元逻辑。
  • 对可空 Bool 的扩展可以改进可空 Bool,但不能解决所有问题。
  • SmartBool 以最佳方式实现了三元逻辑。
  • SmartBool 的逻辑与可空 Bool 的逻辑不同。

Using the Code

代码包含示例和基准测试。项目使用 Microsoft Visual Studio Community 2015 创建。两个项目都需要安装 SSTypesLT 库。SmartBoolBenchMark 项目还需要 BenchmarkDotNet 库。

要安装库,您可以使用 Microsoft Visual Studio 中的 NuGet 程序包管理器控制台。可以通过用户菜单“工具/NuGet 程序包管理器/程序包管理器控制台”激活 NuGet 程序包管理器控制台。

要安装 SSTypesLT 库,请输入以下命令并按 **Enter** 键。

Install-Package SSTypesLT

要安装 BenchmarkDotNet 库,请输入以下命令并按 **Enter** 键。

Install-Package BenchmarkDotNet

对于 .NET Framework 4.0 或更高版本,您可以使用版本 0.9.7,通过以下命令安装:

Install-Package BenchmarkDotNet -Version 0.9.7

代码可从本页顶部的链接下载。

历史

  • 2019 年 4 月 20 日 - 初稿发布。
© . All rights reserved.