有没有更好的方法来重写这个丑陋的开关和if语句组合?

时间:2014-02-06 05:59:54

标签: c++ if-statement switch-statement

基本上我有一个伽玛探测器系统,每个探测器分成4个晶体,在只有2个晶体记录命中的情况下,我们可以确定该对是否垂直或平行于产生的反应平面。伽马射线。在为此写出逻辑的过程中,我写了一个巨大而丑陋的开关语句组合,在每个探测器中检查晶体数的组合(在整个探测器阵列及其晶体中是唯一的)。 这是代码,包括有问题的函数。

//The Parallel and Perpendicular designations are used in addition to the Double
//designation for the 90 degree detectors if we get a diagonal scatter in those detectors
//then we use the Double designation
enum ScatterType{Single, Double, Triple, Quadruple, Parallel, Perpendicular};

ScatterType EventBuffer::checkDoubleGamma(int det)
{
    int num1=evList[crysList[0]].crystalNum;
    int num2=evList[crysList[1]].crystalNum;

    switch(det)
    {
    case 10: //first of the 90 degree detectors
        if( (num1==40 && num2==41) || //combo 1
            (num1==41 && num2==40) || //combo 1 reverse
            (num1==42 && num2==43) || //combo 2
            (num1==43 && num2==42)   )//combo 2 reverse
        { return Parallel; }
        else if( (num1==40 && num2==42) || //combo 1
                 (num1==42 && num2==40) || //combo 1 reverse
                 (num1==41 && num2==43) || //combo 2
                 (num1==43 && num2==41)   )//combo 2 reverse
        { return Perpendicular; }
        else
        { return Double;}
        break;
    case 11: //second of the 90 degree detectors
        if( (num1==44 && num2==45) || //combo 1
            (num1==45 && num2==44) || //combo 1 reverse
            (num1==46 && num2==47) || //combo 2
            (num1==47 && num2==46)   )//combo 2 reverse
        { return Parallel; }
        else if( (num1==44 && num2==47) || //combo 1
                 (num1==47 && num2==44) || //combo 1 reverse
                 (num1==45 && num2==46) || //combo 2
                 (num1==46 && num2==45)   )//combo 2 reverse
        { return Perpendicular; }
        else
        { return Double;}
        break;
    case 13: //third of the 90 degree detectors
        if( (num1==52 && num2==53) || //combo 1
            (num1==53 && num2==52) || //combo 1 reverse
            (num1==54 && num2==55) || //combo 2
            (num1==55 && num2==54)   )//combo 2 reverse
        { return Parallel; }
        else if( (num1==52 && num2==55) || //combo 1
                 (num1==55 && num2==52) || //combo 1 reverse
                 (num1==53 && num2==54) || //combo 2
                 (num1==54 && num2==53)   )//combo 2 reverse
        { return Perpendicular; }
        else
        { return Double;}
        break;
    case 14: //fourth of the 90 degree detectors
        if( (num1==56 && num2==57) || //combo 1
            (num1==57 && num2==56) || //combo 1 reverse
            (num1==58 && num2==59) || //combo 2
            (num1==59 && num2==58)   )//combo 2 reverse
        { return Parallel; }
        else if( (num1==56 && num2==59) || //combo 1
                 (num1==59 && num2==56) || //combo 1 reverse
                 (num1==57 && num2==58) || //combo 2
                 (num1==58 && num2==57)   )//combo 2 reverse
        { return Perpendicular; }
        else
        { return Double;}
        break;
    default:
        throw string("made it to default case in checkDoubleGamma switch statement, something is wrong");
        break;
    }
}

我知道,因为晶体数是全局的而不是每个探测器,我可以取消switch语句,并且有一组由条件或语句链接的大量条件,基本上将事物减少到3个控制路径,一个返回并行一个返回Perpendicular,一个返回Double,而不是12个控制路径,每个控制路径有4个。我最初写它是因为它没有思考,并且诚实地考虑它,这种方法减少了布尔语句的平均案例数量。

我刚刚开始研究如何通过启用evList[crysList[0]].crystalNum来提高效率,我可以减少相当多的评估,产生这个:

ScatterType EventBuffer::checkDoubleGamma()
{
    int crysNum = crysList[1].crystalNum;
    switch(evList[crysList[0]].crystalNum)
    {
    case 40:
        if (crysNum == 41) {return Parallel;}
        else if (crysNum == 42) {return Perpendicular;}
        else {return Double;}
        break;
    case 41:
        if (crysNum == 40) {return Parallel;}
        else if (crysNum == 43) {return Perpendicular;}
        else {return Double;}
        break;
    case 42:
        if (crysNum == 43) {return Parallel;}
        else if (crysNum == 40) {return Perpendicular;}
        else {return Double;}
        break;
    case 43:
        if (crysNum == 42) {return Parallel;}
        else if (crysNum == 41) {return Perpendicular;}
        else {return Double;}
        break;
    case 44:
        if (crysNum == 45) {return Parallel;}
        else if (crysNum == 47) {return Perpendicular;}
        else {return Double;}
        break;
    case 45:
        if (crysNum == 44) {return Parallel;}
        else if (crysNum == 46) {return Perpendicular;}
        else {return Double;}
        break;
    case 46:
        if (crysNum == 47) {return Parallel;}
        else if (crysNum == 45) {return Perpendicular;}
        else {return Double;}
        break;
    case 47:
        if (crysNum == 46) {return Parallel;}
        else if (crysNum == 44) {return Perpendicular;}
        else {return Double;}
        break;
    case 52:
        if (crysNum == 53) {return Parallel;}
        else if (crysNum == 55) {return Perpendicular;}
        else {return Double;}
        break;
    case 53:
        if (crysNum == 52) {return Parallel;}
        else if (crysNum == 54) {return Perpendicular;}
        else {return Double;}
        break;
    case 54:
        if (crysNum == 55) {return Parallel;}
        else if (crysNum == 53) {return Perpendicular;}
        else {return Double;}
        break;
    case 55:
        if (crysNum == 54) {return Parallel;}
        else if (crysNum == 52) {return Perpendicular;}
        else {return Double;}
        break;
    case 56:
        if (crysNum == 57) {return Parallel;}
        else if (crysNum == 59) {return Perpendicular;}
        else {return Double;}
        break;
    case 57:
        if (crysNum == 56) {return Parallel;}
        else if (crysNum == 58) {return Perpendicular;}
        else {return Double;}
        break;
    case 58:
        if (crysNum == 59) {return Parallel;}
        else if (crysNum == 57) {return Perpendicular;}
        else {return Double;}
        break;
    case 59:
        if (crysNum == 58) {return Parallel;}
        else if (crysNum == 56) {return Perpendicular;}
        else {return Double;}
        break;
    default:
        throw string("made it to default case in checkDoubleGamma switch statement, something is wrong");
        break;
    }
}

问题仍然存在,是否有任何技巧可以缩短它?更高效?更具可读性?

提前致谢!

3 个答案:

答案 0 :(得分:4)

我认为您可以将几乎所有内容移动到一个简单的表中,然后使用单个表查找。我没有详细研究过你的条件,但看起来这样的事情就可以了:

// fill the following table in advance using your existing function, or hard-code the 
// values if you know they will never change:
ScatterType hitTable[60][60];


ScatterType EventBuffer::checkDoubleHit(int det)
{
    // read the crystal Nums once:
    unsigned a = evList[cryList[0]].crystalNum;
    unsigned b = evList[cryList[1]].crystalNum;

    switch(det)
    {
    case 10:
    case 11:
    case 13: 
    case 14:
      // better safe than sorry:
      assert (a < 60);
      assert (b < 60);
      return hitTable[a][b];
    break;

    default:
        throw string("made it to default case in checkDoubleHit switch statement, something is wrong");
        break;
    }
}

答案 1 :(得分:1)

使更短的一种解决方案是在比较/切换之前对晶体值进行排序

int nummin=evList[crysList[0]].crystalNum;
int nummax=evList[crysList[1]].crystalNum;

if (nummin > nummax) 
{
    tmp = nummin;
    nummin = nummax;
    nummax = tmp;
}
// or like Jarod42 said: std::minmax(numin, numax);

if ((nummin == 40 && nummax == 41) ||  // no need to compare the reverse
    (nummin == 42 && nummax == 43))    // and reduce haft of the comparison
{ ... }

答案 2 :(得分:0)

结合4x4查找表和按位操作的解决方案。

说明: 对于所有四种情况,这都是相同的,所以让我们看看det=10

的情况

在这种情况下,有趣的数字是{40,41,42,43}。 如果我们在二进制表示中查看这些数字,就会出现一个漂亮的模式。

上面的数字是我们的面具,由det*4计算。 所以在这种情况下我们的面具是10*4=40

 0b101000 (40)  0b101000       0b101000       0b101000
^0b101000 (40) ^0b101001 (41) ^0b101010 (42) ^0b101011 (43)
=0b000000      =0b000001      =0b000010      =0b000011

在掩码和我们允许的数字之间执行xor(^)之后,我们看到它们在集合{0,1,2,3}中都有一个值。 如果是,例如num1 ^ mask < 4,这意味着,只有最右边的两个位与mask不同,或num1最多比mask大3。 如果num1 < mask最右边两个位置的至少一些位将翻转,num1 ^ mask将至少为4。 因此,如果num1 ^ mask < 4为真,则num1位于{40,41,42,43}。

此外,因为它位于{0,1,2,3},我们也可以将它用作查找表中的索引。

现在,如果先前的计算对于num1num2都是正确的,那么我们有一个查找表的组合索引。

int checkDoubleGamma(int det){
    static const int hitTable[4][4] = {
        {Double, Parallel, Perpendicular, Double},
        {Parallel, Double, Double, Perpendicular},
        {Perpendicular, Double, Double, Parallel},
        {Double, Perpendicular, Parallel, Double}
    };

    const int num1 = evList[crysList[0]].crystalNum;
    const int num2 = evList[crysList[1]].crystalNum;

    switch(det) {
    case 10: //0b101000
    case 11: //0b101100
    case 13: //0b110100
    case 14: //0b111000
    {
        const unsigned int mask = 4 * det;
        const unsigned int a = num1 ^ mask;
        if(a < 4){
            const unsigned int b = num2 ^ mask;
            if(b < 4)
                return hitTable[a][b];
        }
        return Double;
    }
    default:
        throw string("made it to default case in checkDoubleGamma switch statement, something is wrong");
        break;
    }
    //Never reaches here
}

编辑:这可以缩减为1x4查找表

如果我们查看a^b的xor-table,我们会发现它看起来与我们的4x4查找表非常相似。

 ^ 00 01 10 11
00 00 01 10 11
01 01 00 11 10
10 10 11 00 01 
11 11 10 01 00

这给了我们

00,11 = Double
01    = Parallel
10    = Perpendicular 

因此我们可以将旧的查找表减少到1x4查找表,并使用a^b作为索引。

通过仔细观察a^b,我们发现num1^mask^num2^mask等于num1^num2,然后我们将其用作索引并保存xor指令。

这仍会检查num2是否在{40,41,42,43}内。如果num1仅与最右边两位中的mask不同,并且num2仅与最右边两位中的num1不同,则num2仅与mask不同在最右边的两个位中{1}}。因此,忽略num2^mask不会改变程序的行为。

int checkDoubleGamma(int det){
    static const int hitTable[4] = {Double, Parallel, Perpendicular, Double};

    const int num1 = evList[crysList[0]].crystalNum;
    const int num2 = evList[crysList[1]].crystalNum;

    switch(det) {
    case 10: //0b101000
    case 11: //0b101100
    case 13: //0b110100
    case 14: //0b111000
    {
        const unsigned int mask = 4 * det;
        const unsigned int a = num1 ^ mask;
        if(a < 4){
            const unsigned int b = num1 ^ num2;
            if(b < 4)
                return hitTable[b];
        }
        return Double;
    }
    default:
        throw string("made it to default case in checkDoubleGamma switch statement, something is wrong");
        break;
    }
    //Never reaches here
}