【问题标题】:Checking only specific booleans in an array or list仅检查数组或列表中的特定布尔值
【发布时间】:2017-12-05 16:11:09
【问题描述】:
 if (boolThing[0] == true 
        && boolThing[2] == true 
        && !boolThing[1] 
        && !boolThing[3] 
        && !boolThing[4])
    {
        Debug.Log("Hey");
    }

我想学习如何做到这一点的不同方法。

当 0 和 3 为真而其余为假时,代码会执行某些操作。

是否有“更清洁”或“更有效”的方法来做到这一点?

【问题讨论】:

  • boolThing[0] == true 只能是boolThing[0]== true 是多余的。

标签: c# boolean


【解决方案1】:

您不需要if(boolThing[0]==true)。您可以只使用 `if (boolThing[0]))

您还可以使用二元运算符来同时计算多个布尔值。

if((bools[0]&bools[3]) && !(bools[1]|bools[2]|bools[4]|bools[5])

您可以使用BitArray 轻松处理布尔值集合,这样效率会更高。

最有效的方法是使用数字类型。

最简单的方法是使用带有标志的枚举。您可以使用二元运算符来设置或清除枚举的标志。

[Flags]
public enum MyBoolFlag
{
    None,
    Bool0 = 1<<0,
    Bool1 = 1 << 1,
    Bool2 = 1 << 2,
    Bool3 = 1 << 3,
    Bool4 = 1 << 4,
    Bool5 = 1 << 5,
    //etc
}

你使用二进制操作来设置标志。

MyBoolFlag myFlag = MyBoolFlag.Bool0|MyBoolFlag.Bool1|MyBoolFlag.Bool3;

在您的代码中,您现在可以检查:

if(myFlag == MyBoolFlag.Bool0|MyBoolFlag.Bool3)
{

}

您可以为各种条件定义常量。

const MyBoolFlag myCondition1 = MyBoolFlag.Bool0|MyBoolFlag.Bool3;

然后检查:

if(myFlag == myCondition1)
{

}

你也可以做一些额外的工作,直接使用原生类型,比如 byte 和 int。

您可以使用单个字节将 8 个布尔值保存为标志掩码,每个掩码代表 2 的幂。一个 uint32 可以保存 32 个,一个 uint64 可以保存 64 个布尔值。

这具有巨大的性能提升,因为布尔值存储为 uint32,这意味着它使用 4 个字节的数据,并且当您使用位询问布尔标志时,您可以将 32 个布尔值打包到一个 uint32 中。

程序也将执行得更快,因为 CPU 使用本地机器字在单个操作中评估 myFlag == myCondition1。否则需要许多指令来将查找布尔值放入数组中并进行比较。 .Net 还会进行一些完整性检查,确保您的数组不为空,索引不超出范围,然后需要移动指针以查找这些值。

为方便起见,您可以从布尔数组创建位数组,然后使用它来初始化数值类型数组并设置相应的标志。

例如:

bool[] bools = new bool[] { true, false, false, true, false, false };
BitArray bitArray = new BitArray(new bool[] { true, false, false, true, false, false });
int[] array = new int[1];
bitArray.CopyTo(array, 0);
int value = array[0];

如果 bools[0] = true 且 bools[3] true,则“值”将是 9。

所以你的代码应该是:

if(value==9) // bools[0]&bools[1]==true;

您还可以创建常量掩码来检查不同的数值,以检查您的数值中是否设置了不同的位或使用二进制运算来操作位。

使用二元运算设置数值可以提高效率。

uint32 myFlag= 1|(1<<3);

【讨论】:

  • 在您的示例 (bools[0]&amp;bools[3]) &amp;&amp; !(bools[1]|bools[2]|bools[4]|bools[5]) 开头,不需要“单个”&amp;| 运算符。太浪费了,直接用&amp;&amp;||吧。
  • &amp;&amp; 可以工作,但需要三个操作来评估第一组是否为真,但 &amp; 需要一个。对于&amp;&amp;,第一个操作检查 bools[0] 是否为真,第二个操作检查 bools[1] 是否为真,第三个是括号内的 &amp;&amp; 运算符。使用&amp; 二进制操作会将 bools[0] 和 bools[1] 组合成一个结果,然后评估是否为真。您可以对此进行编码并查看 VS 中的反汇编进行验证。由于使用||,下半年还会有更多的jmp。您可以使用这两种方法运行一个循环进行数百万次验证。
  • 有趣。在你的百万循环验证中,bools的内容每次都一样吗?如果您在括号内执行,为什么不在“最终”中间操作中也使用&amp;?短路和非短路的混合看起来很奇怪。如果我以子表达式bools[1]|bools[2]|bools[4]|bools[5]) 为例,您必须检查所有数组条目,即使bools[1] 已经是true。但是检查一个数组条目是非常快的,所以这是一种特殊情况。在|| 之前,| 链要多长时间更有效?
  • 你是对的,整个表达式可能是一个二元运算。关键是当你的代码块使用二进制操作执行多个语句时,它通常比使用 && 或 || 的语句执行得更快。即使它们短路。原因是短路操作实际上被评估为单独的 IF 语句,这些语句在汇编中变成了跳转语句。循环中的跳转语句是可怕的。这就是程序员会展开循环的原因,因为循环控制本身就是一个跳转语句,它会减慢代码执行速度。
  • 当您将 if/jump 语句放在索引查找操作之间时,问题会更加严重,因为在 .Net 中,除非代码经过优化,否则不会直接查找指向数组的指针与本机代码中的方式相同。
【解决方案2】:

您可以将变量放在适当的列表中,然后测试每个 List 是否仅包含特定值的元素(truefalse):

List<bool> thingsTestedForTrue = new List<bool> { boolThing[0], boolThing[1], boolThing[2] };
List<bool> thingsTestedForFalse = new List<bool> { boolThing[3], boolThing[4], boolThing[5] };

if(thingsTestedForTrue.All(thing => thing == true) && thingsTestedForFalse.All(thing => thing == false) 
{
    // All values passed
}

【讨论】:

    【解决方案3】:

    你可以这样做,命名空间 System.Linq in Unity

    if(boolThing.SequenceEqual(new bool[] { true, false, true, false, false})
    {
    Debug.Log("Hey");
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-01-24
      • 1970-01-01
      • 1970-01-01
      • 2013-04-25
      • 1970-01-01
      • 1970-01-01
      • 2018-04-20
      相关资源
      最近更新 更多