如何将许多 if 语句压缩成更小且更易读的内容?

How to condense many if-statements into something smaller and more readable?

我正在为生活游戏编写代码。其中一个功能应该是 "evolve" 当前的一组单元格。这意味着我必须有一些条件,如果 if 语句满足条件,则单元格将变为 DEAD 或 ALIVE 单元格。

但是,只有一些下一个单元格正在初始化,所以我必须将其余下一个单元格设置为 DEAD,这些单元格不受条件影响。

由于有四个条件,我想将我的 if 语句压缩到尽可能少的数量。

条件是:

我已经尝试将尽可能多的条件拟合到一个 if 语句和一个 else 语句中,这意味着如果没有单元格满足条件,它将自动设置为 DEAD。这样 field[i][j].next 中的所有细胞要么死要么活。

我想提一下,数组 field[i][j].current 已经用 DEAD 和 ALIVE 单元格进行了初始化。

  void evolve(const int rows,const int cols,cell field[rows][cols], int 
    NeighbourCounter[i][j]){

       CellNeighbour(rows, cols,field,NeighbourCounter);

       for(int i = 0;i<rows;i++){
         for(int j =0;j<cols;j++){
           if(field[i][j].current == ALIVE  && NeighbourCounter[i][j] < 2){
              field[i][j].next == DEAD;
           }
           if(field[i][j].current == ALIVE && NeighbourCounter[i][j] == ||NeighbourCounter[i][j] == 2){
               field[i][j].next = ALIVE;
           } 
           if(field[i][j].current == ALIVE && NeighbourCounter[i][j] >= 4){
                field[i][j].next = DEAD;
           }
           if(field[i][j].current == DEAD && NeighbourCounter[i][j] == 3){
                field[i][j].next = ALIVE;
           }
         }
     else{
            field[i][j].next = DEAD;
     }
    }

NeighbourCounter 是一个数组,用于计算每个单元格有多少个 ALIVE 邻居。

预期的输出应该是 field[rows][cols] 应该更新并且更新的版本存储在 ``field[rows][cols].next` 中。

How to condense many if-statements into something smaller and more readable?

您使用的代码格式不正确。 例如,不清楚以下 else 语句与哪个 if 语句配对。

 else{
        field[i][j].next = DEAD;
 }

错误的代码格式通常是许多错误(包括逻辑错误)的根源。:)

所有这些 if 语句

      if(field[i][j].current == ALIVE  && NeighbourCounter[i][j]<2){
           field[i][j].next == DEAD;
        }
         if(field[i][j].current == ALIVE && NeighbourCounter[i][j] ==3 
||NeighbourCounter[i][j] ==2 ){
          field[i][j].next = ALIVE;
        }
         if(field[i][j].current == ALIVE && NeighbourCounter[i][j] >= 4 
){
           field[i][j].next = DEAD;
         }
//...

可以改写成

if ( field[i][j].current == ALIVE )
{
    if ( NeighbourCounter[i][j] < 2 )
    {
        field[i][j].next == DEAD;
    }
    else if ( NeighbourCounter[i][j] < 4 )
    {
        field[i][j].next = ALIVE; 
    }
    else 
    {
        field[i][j].next = DEAD;     
    }
}
else if ( field[i][j].current == DEAD )
{
    if ( NeighbourCounter[i][j] == 3 )
    {
        field[i][j].next = ALIVE;
    }    
}
// ... 

这使代码更具可读性。

作为替代方法,您可以使用 switch 语句,例如 this

switch ( field[i][j].current )
{
    case ALIVE:
    {
        // ...
    }

    case DEAD:
    {
        // ...
    }

    default:
    {
        // ...
    }
}

简单地 switch 邻居计数的值可能是最简单和最清楚的,如下所示:

for (int i = 0;  i < rows;  ++i) {
    for (int j = 0;  j < cols;  ++j) {
        switch (NeighbourCounter[i][j]) {
        case 0:
        case 1:
            /* cell dies without enough support */
            field[i][j].next = DEAD; /* I assume you meant =, not == */
            break;
        case 2:
            /* two neighbours can support a live cell */
            field[i][j].next = field[i][j].current;
            break;
        case 3:
            /* three neighbours can resurrect a dead one */
            field[i][j].next = ALIVE;
            break;
        case 4:
        case 5:
        case 6:
        case 7:
        case 8:
            /* more than three is a crowd */
            field[i][j].next = DEAD;
            break;
        default:
            fprintf(stderr, "Invalid value %d from NeighbourCounter\n",
                    NeighbourCounter[i][j]);
        }
    }
}

您会看到 case 2 是唯一需要 current 值的;所有其他的只取决于邻居的数量。

一种强大的函数编码技术是lookup tables。邻居数限制在 0...9 范围内,状态数为 2,因此您可以使用大小为 20:

的查找 table
bool NewStateLUT[2][10] = {
    {0, 0, 0, 1, 0, 0, 0, 0, 0, 0}, // new states for dead cells
    {0, 0, 1, 1, 0, 0, 0, 0, 0, 0}  // new states for alive cells
};

使用这个的代码table:

whatever.next = NewStateLUT[whatever.current][NeighbourCounter];

此代码假定 DEAD 为 0,ALIVE 等于 1。如果 DEADALIVE 是枚举数,则可能需要调整语法。


由于LUT只包含0和1,所以用位图代替数组很容易:

uint32_t NewStateBitmap = 0b0000000100'0000001100;
whatever.next = (NewStateBitmap >> (whatever.current * 10 + NeighbourCounter)) & 1;

如果一个细胞有 3 个邻居,或者如果它目前还活着并且有两个邻居,那么它在下一代还活着。

这也许有点异端,但我想知道常量 ALIVE 和 DEAD 是否没有妨碍。另一种方法是将字段 current 重命名为 now_alive 并在 next_alive 旁边,然后在其中存储 0 或 1。

那么next alive的计算就可以写成

C->next_alive = (N == 3) || (C->now_alive+N) == 3;

其中 C 指向单元格,N 是它的邻居数。