如何简化嵌套的if/else语句并重复使用结果?

32

我试图简化以下内容:

function handleDirection(src) {
  if (src === 'left') {
    if (inverse) {
      tracker--;
    } else {
      tracker++;
    }
  } else {
    if (inverse) {
      tracker++;
    } else {
      tracker--;
    }
  }
}

为了减少条件语句的数量。 src 的值始终为 'left''right'


10
现在有多种答案-需要记住的一件事是可维护性,这包括你自己是否能理解这段代码在下周的作用。确保选择一种逻辑形式,一目了然地清楚它所做的事情-如果原始问题中的长表达式是最清晰的,请坚持使用它。 - James Thorpe
14
我投票将此问题关闭,因为它属于https://codereview.stackexchange.com/。 - Gabriele Petrioli
10
жіЁпјҡдҪ зҡ„еҮҪж•°дҪҝз”ЁдәҶ3дёӘеҸҳйҮҸ(srcгҖҒinverseе’Ңtracker)пјҢдҪҶеҸӘжңү1дёӘеҸӮж•°(src)дё”жІЎжңүиҝ”еӣһеҖјгҖӮеӣ жӯӨпјҢж— и®әдҪ еҰӮдҪ•з»“жһ„ifsпјҢе®ғйғҪдёҚдјҡйҖҡиҝҮжҲ‘зҡ„д»Јз Ғе®ЎжҹҘгҖӮ - Peter B
1
@PeterB 我基本上同意,但值得注意的是上下文很关键。如果这是对象中的一个方法,那么可能没问题。这可以通过命令(“left”/“right”)来操作某种光标(tracker),对象本身具有一个标志,它将朝相反方向移动(invert)。然而,作为自由浮动的函数,这确实是不好的,因为您正在操作一些不一定相关的全局状态。 - VLAZ
1
感谢大家,目前这是一个自由浮动的函数;我提出这个问题的主要目的是为了捕捉编写此条件的最有效方法。我经常发现像这样简单的事情会让我犯错,所以在深入研究之前现在提出这个问题是有用的。下面的答案都是有趣的方法,我必须测试一下。 - Rebecca O'Riordan
显示剩余5条评论
14个回答

55

您可以查看第一次检查的结果。

这是一个异或检查。

// typeof inverse === 'boolean'

function handleDirection(src) {
    if (src === 'left' === inverse) {
        tracker--;
    } else {
        tracker++;
    }
}

检查按此顺序评估表达式(src === 'left') === inverse:

src === 'left' === inverse
---- first ---             returns a boolean value
--------- second --------- take result of former check & compairs it with another boolean

2
^^ 只在这种特殊情况下。 - Nina Scholz
9
我完全同意@marcelm的看法。尽管这个回答看起来很棒,但它所发生的事情并不是立即明显的。 - Marie
6
只有在你真正需要性能提升或只与能够直观理解并产生这种结果的人一起工作时,才会使用这种方法。另一种方法可能会更长,但对于普通开发者来说更易读。 - Frank Hopkins
1
如果你想用三元运算符实现,我认为增量(tracker += (src == 'left') == inverse ? -1 : +1;)更清晰。 - Charlie Harding
1
@Darkwing 性能提升?什么性能提升?你有测量过吗? - phoog
显示剩余6条评论

18
function handleDirection(src) {
   var movement = 1;
   if(src === 'left')
     movement = -1;

   if(inverse)
     tracker += movement;
   else
     tracker -= movement;
}

2
这在我看来创建了一个不必要的额外变量... 在这种情况下,使用 tracker--tracker++ 是正确的增加和减少变量的方式。如果希望以超过一个的方式增加或降低变量,则可能会有所帮助。 - MagicLegend
3
实际上,我认为变量有助于将解决方案带入“真实世界”的平衡状态。其他答案侧重于“效率”,这在如此简单的情况下可能并不相关。解释器不需要帮助阅读,但是人类需要。 尽管我已经点赞了,但我会进一步给变量一个更有意义的名称,比如adjustmentmovement - TheRubberDuck

11

您甚至可以只用一行代码完成它:

function getDirectionOffset(src) {
  tracker += (src === 'left' ? 1 : -1) * (inverse ? -1 : 1);
}

7

这可以简化为一个三元表达式,根据状态返回1-1,然后将其添加到 tracker 中。

function handleDirection(src) {
  var delta = (src === 'left' && inverse) || (src !== 'left' && !inverse) ? -1 : 1;
  tracker += delta;
}

这可以进一步简化,使用@NinaScholz在她的回答中指出的逻辑:
function handleDirection(src) {
  var delta = (src === 'left') === inverse ? -1 : 1;
  tracker += delta;
}

6
假设“inverse”是一个标志,您只需设置一次,然后无需每次考虑它,您可以计算其影响一次并直接使用它,这将减少您的代码分支和逻辑。如果您想随时更改它,则可能需要分离计算逻辑,以便重复使用。
然后,您还可以将移动方向提取为一个独立的函数,您的“handleDirection”变得非常简单-根据“src”和“invert”计算所需的方向。

let tracker = 0;

//extract logic for the movement offset based on direction
function getDirectionOffset(src) {
  return src === 'left' ? 1 : -1;
}

//have a setter for the invert property
function setInverse(isInverse) {
  movementModifier = isInverse ? -1 : 1
}

//declare the variable dependent on the inverse property
let movementModifier;

//initialise movementModifier variable
setInverse(false);

function handleDirection(src) {
  const offset = getDirectionOffset(src) * movementModifier;
  
  tracker += offset;
}


// usage
setInverse(true);

handleDirection("left");
handleDirection("left");
handleDirection("right");

console.log(tracker);

鉴于此,所有这些都表明你不应该使用一个函数,或者你应该以不同的方式使用它。可以将所有这些功能收集在一个类中,或者通过在函数之间传递信息来避免使用全局变量。下面是一个面向对象实现该概念的示例:

class TrackerMover {
  constructor(inverse) {
    this.tracker = 0;
    this.movementModifier = inverse ? 1 : -1
  }
  
  handleDirection(src) {
   const offset = this.getDirectionOffset(src) * this.movementModifier;

    this.tracker += offset;
  }
  
  getDirectionOffset(src) {
    return src === 'left' ? -1 : 1;
  }
  
  getPosition() {
    return this.tracker;
  }
}


//usage
const mover = new TrackerMover(true);

mover.handleDirection("left");
mover.handleDirection("left");
mover.handleDirection("right");

console.log(mover.getPosition())

顺便提一下,另一种选择是不必每次都计算移动。实际上,你每次都知道发生了什么 - 实际上,你有一个真值表,其中输入为 src === leftinverse,输出为如何修改你的跟踪。

+--------+------------+--------+
| isLeft | isInverted | Offset |
+--------+------------+--------+
| true   | true       |     -1 |
| true   | false      |      1 |
| false  | true       |      1 |
| false  | false      |     -1 |
+--------+------------+--------+

所以,你只需将那张表格放进去即可。

let tracker = 0;
let invert = false;

const movementLookupTable = {
  "true": { },
  "false": { },
}

//it can be initialised as part of the above expression but this is more readable
movementLookupTable[true ][true ] = -1;
movementLookupTable[true ][false] = 1;
movementLookupTable[false][true ] = 1;
movementLookupTable[false][false] = -1;

function handleDirection(src) {
  const offset = movementLookupTable[src === "left"][invert];

  tracker += offset;
}


// usage
invert = true;

handleDirection("left");
handleDirection("left");
handleDirection("right");

console.log(tracker);

在这种情况下,这种方法可能有些过度,但如果有更多的标记(包括标记的更多)和/或最终状态,这种方法可能会很有用。例如,也许你想引入四个方向,但如果它是updown,就不修改tracker值。

+-----------+------------+--------+
| direction | isInverted | Offset |
+-----------+------------+--------+
| left      | true       |     -1 |
| left      | false      |      1 |
| right     | true       |      1 |
| right     | false      |     -1 |
| up        | false      |      0 |
| up        | true       |      0 |
| down      | false      |      0 |
| down      | true       |      0 |
+-----------+------------+--------+

如您所见,现在不仅可以处理布尔值,还可以处理任何值。使用表格,您还可以将invert更改为类似于windDirection的内容,因此如果移动是leftwindDirectionright,则结果就像现在这样,但您也可以有left方向和风向left的情况,这样您可以移动更远。或者您可以向up移动,而风向是left,因此tracker(此时的X坐标)实际上将被修改。

+-----------+---------------+---------+
| direction | windDirection | OffsetX |
+-----------+---------------+---------+
| left      | right         |      -1 |
| left      | up            |       1 |
| left      | down          |       1 |
| left      | left          |       2 |
| right     | up            |      -1 |
| right     | down          |      -1 |
| right     | right         |      -2 |
| right     | left          |       1 |
| up        | up            |       0 |
| up        | down          |       0 |
| up        | left          |       1 |
| up        | right         |      -1 |
| down      | up            |       0 |
| down      | down          |       0 |
| down      | left          |       1 |
| down      | right         |      -1 |
+-----------+---------------+---------+

考虑到四个方向和四个风向的逻辑,阅读和将来维护的工作可能会非常烦人。但是,如果您只有一个查找表,那么这很容易,并且您甚至可以轻松扩展它以处理对角线(假设它们通过 0.5 而不是 1 更改值),只要您从表中获取值,您的算法就不会真正关心。


3

这个条件语句只有一个,我认为它比其他答案更直观易懂:

function handleDirection(src) {
    if (
        ((src === 'left') && !inverse) ||
        ((src === 'right') && inverse)
    ) {
        tracker++;
    }
    else {
        tracker--;
    }
}

3

如果其中一个src == leftinverse为真,则希望增加跟踪器,否则减少跟踪器,这正是“XOR”^运算符所实现的功能:

function handleDirection(src) {
    if (src === 'left' ^ inverse) {
        tracker++;
    } else {
        tracker--;
    }
}

你可以使用三元表达式来进一步缩减代码:

你可以使用三元表达式来进一步缩减代码:

function handleDirection(src) {
    tracker += src === 'left' ^ inverse ? 1 : -1;
}

或者如果您想避免任何条件、隐式强制类型转换和“聪明”的算术操作:

function handleDirection(src) {
    tracker += 1 - 2 * (src === 'right' ^ inverse); // either 1-0=1 or 1-2=-1
}

1
你的逻辑是反过来的,我不喜欢第三个例子,但是因为实际使用了专门为此构建的运算符,所以加一分。 - Jacob Raihle
2
@JacobRaihle 谢谢,我已经修复了逆向逻辑。第三个例子中的“聪明”引号是讽刺引号,除非唯一的目的是扮演聪明人,否则我不建议使用它。 - Aaron

3

您根本不需要任何if语句。只需使用三元运算符,通过计算srcinverse的正负增量即可执行相同的操作。

function handleDirection(src) {
    tracker += (src == "left" ? 1 : -1) * (inverse ? -1 : 1);
};

顺便说一下,为了效率起见,我建议直接使用数字增量/减量,而不是需要额外处理才能解码的字符串。您可以使用常量来实现相同的可读性:

另外,反转也可以优化为在1(未反转)和-1(已反转)之间切换的数值。

const left = 1;
const right = -1;
var direction = 1;

function handleDirection(src) {
    tracker += src * direction;
}

function reverse() { // (Example)
    direction = direction * -1;
}

即使“right”和“left”这些关键字来自某种文本用户输入,你也可以从字典中简单地翻译它们:
const steps = {
    left = 1;
    right = -1;
};

function handleDirection(src) {
    tracker += steps[src] * direction;
}

2

我不喜欢使用else,如果可能的话尽量避免嵌套。我认为这种方式更自然地传达了“反向”的概念:inverse

function handleDirection(src) 
{
    let change = 1;

    if ('right' == src)
        change = -1;

    if (inverse)
        change = -change;

    tracker += change;
}

2
你可以使用短路语法或三元运算符。
// by using short circuiting
    function handleDirection(src) {
       if (src == 'left') tracker = inverse && tracker-1 || tracker +1
       else  tracker = inverse && tracker+1 || tracker -1
    }
// by using ternary operator
 function handleDirection(src) {
       if (src == 'left') tracker = inverse ? tracker-1 : tracker +1
       else  tracker = inverse ? tracker+1 : tracker -1
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接