简化JavaScript的if-else结构

17

我现在拥有的代码是:

 var level = function (d) {
    if (value(d) > median + stdev) {
        return 1;
    } else if (value(d) > median) {
        return 2;
    } else if (value(d) > median - stdev) {
        return 3;
    } else {
        return 4;
    }
 };

有没有更好的方法做这件事?


13
这是一个更适合在http://codereview.stackexchange.com上询问的问题。 - Matt Ball
想知道三元运算符。但这里怎么用? - internals-in
一个嵌套的三元运算符似乎是可行的,尽管它会更难阅读。 - everconfusedGuy
1
你对“更好”的标准是什么?你为什么要重新评估价值多达三次的原因是什么? - symcbean
使用 switch 语句! - nathan hayfield
这很容易阅读和理解。你为什么需要修改它? - zzzzBov
14个回答

15

当然,多次调用value(d)是可以避免的。

此外,你可以利用对称性稍微缩短一下:

  var level = function (d) {
    //
    //               -std    median  +std
    // ----------------|-------|-------|------------------
    // 4444444444444444 3333333 2222222 111111111111111111
    //
    var i = Math.floor((median - value(d)) / stddev) + 3;
    return Math.max(1, Math.min(4, i));
  };

对于一个真实项目,这可能不是一个好主意...我没有测试过,但我不会感到惊讶,如果发现这段代码比你问题中的原始代码更慢,而且我肯定觉得它更难以维护。

需要注意的是,除了一次性的丢弃脚本之外,通常的代码只写一次,并且被多次阅读(用于维护、改进或调试),因此“易于阅读”通常比“易于编写”更重要。

当更短意味着“更容易阅读”时,这是一件好事,但当它开始意味着“更难阅读”时,就不是了。


太棒了!非常好的文档/表现,我记得你来自Python答案。 - Grijesh Chauhan
@EricFromSouthPark:简化的原因在于两个区间是相等的,因此将值与中位数之间的差除以区间宽度,你就得到了一个数值整数代码,只需要夹紧即可。 - 6502
7
ASCII艺术很好,但我不认为代码更“好看”。它更“聪明”,行数更少,但它使用了除法和来自Math函数的内容,并且通常不适用于标称尺度(https://en.wikipedia.org/wiki/Level_of_measurement#Nominal_scale)。执行速度并没有更快。重构代码已经不再那么简单,例如如果您想要其他值/其他类型的值或仅包括其他/更多情况...这是代码高尔夫(http://en.wikipedia.org/wiki/Code_golf),但没有改进代码质量。 - mnagel
3
我同意。请仔细检查问题的标题。 - 6502
@6502:问题的标题和结束请求有些不同,而且大家都集中在“短”而不是“好”。这可能是原作者想要的,也可能不是,我无法确定。我认为你的答案很有见地,但我不会将重构应用于我的代码(虽然我会复制文档字符串)。我的评论提供了关于我看到的问题方面的具体论据,并旨在让您思考您在代码中关心的内容。总的来说,我认为这是一个很好的答案,但不是我想要没有评论就离开的答案。 - mnagel
显示剩余3条评论

7
为了让整个套装完整,这里是 @austin 提到的 switch 方法:
var level = function (d) {
  var d = value(d) - median;
  switch (true) {
  case d > stdev : return 1;
  case d > 0:      return 2;
  case d > -stdev: return 3;
  default:         return 4;
  }
};

这种情况下允许使用 < == > 吗?如果可以,则加上加号,否则我会回来给你点踩 :) - Grijesh Chauhan
我相信你不需要进行负面评价。当你第一次看到它时,这是一个令人惊讶的结构。case的定义实际上是一个表达式而不是字面值。有时候可以利用这个特性。 - HBP
我是一名C程序员,这真让我惊讶!这只有在JavaScript中才可能吗?或者Java的新版本也允许这样做吗?因为在很久以前我曾经想过Java中能否存在字符串情况。 - Grijesh Chauhan
1
在像JavaScript这样的解释性语言中做这件事肯定更容易。正如您所知,在C中,case值必须是字面整数值。快速检查显示,在Java中,case值必须是整数或字符串常量。 - HBP
我在其他语言中也看到过这种写法,但听说它会影响性能(至少在JavaScript中是如此)。如果需要多次运行,原始的if/else可能更快。我希望我能在其他语言中也这样做,但我经常使用的语言(C/C++/C#/Java)都不允许这样做。 - Darrel Hoffman

4

这个解决方案更好,但我不建议在生产中使用,因为它有点令人困惑:

4 - [median + stdev, median, median - stdev].filter(function(e, i, a) {
    return value(d) > e;
}).length 

2
是的,但+1肯定使用了最少的字节来存储在我的硬盘上。 - jenson-button-event
@jenson-button-event: 那些是最重要的字节 ;) - Amberlamps
3
我没有点踩,但肯定这个回答和@6502的回答有些难以立即理解。;D - Alberto Zaccagni

3

您可以计算值与中位数之间的差异,这样比较会更简单:

function level(d) {
  var n = value(d) - median;
  if (n > stdev) {
    return 1;
  } else if (n > 0) {
    return 2;
  } else if (n > -stdev) {
    return 3;
  } else {
    return 4;
  }
};

你也可以使用条件运算符来编写,而不是使用if语句:

function level(d) {
  var n = value(d) - median;
  return n > stdev ? 1 :
    n > 0 ? 2 :
    n > -stdev ? 3 :
    4;
  }
};

无论这是否更好是品味的问题,但它更短。

2
“更好”的方式?其实并没有。
可替代的方式 - 是的,有很多种。
一种可能的方式是将条件和结果存储在一个数组中。
var levelFunctions = [
  { func: function(d){ return value(d) > median + stdev; }, val:1},
  { func: function(d){ return value(d) > median ; }, val:2},
  { func: function(d){ return value(d) > median - stdev; }, val:3},
  { func: function(d){ return true; }, val:4}
];

然后只需将该列表作为函数的一部分枚举即可。
var level = function (d) {
    for(var i=0;i<levelFunctions.length;i++){
       if(levelFunctions[i].func(d))
           return levelFunctions[i].val;
    }
 };

比起原来的扩展要容易一点,但是天哪,它丑得像罪恶一样!

如果你真的想使用一个数组,你应该像@amberlamps一样使用一个。 - Bergi

2

我认为很难有改进的空间,而且还要保证与原始情况相同的可读性。如果你真的返回整数,并且它们不仅仅是为了这个例子而存在,那么我建议你返回更有意义的内容。

当然,你可以只计算一次 value(d)

var level = function (d) {
  var dValue = value(d);
  if (dValue > median + stdev) {
    return 1;
  } else if (dValue > median) {
    return 2;
  } else if (dValue > median - stdev) {
    return 3;
  } else {
   return 4;
  }
};

另外,您可能希望避免多次换行,或者也许您不想这样做,对我来说它们是相同的,每个都有优缺点:

var level = function (d) {
  var dValue = value(d),
      code = 4;
  if (dValue > median + stdev) {
    code = 1;
  } else if (dValue > median) {
    code = 2;
  } else if (dValue > median - stdev) {
    code = 3;
  } 
  return code;
};

如果你给code分配一个有意义的名称,那么你为阅读你的代码的人提供了更多的信息。

2

我建议避免多次调用value

function level(d) {
    var diff = value(d) - median;
    if (diff > 0) {
        if (diff > stdev)
            return 1;
        else
            return 2;
    else
        if (diff > -stdev)
            return 3;
        else
            return 4;
}

此外,我已将if-else语句嵌套在一个(希望更有意义的)结构中 - 当然这取决于您的用例。如果返回像 -2 -1 1 2 之类的值可能更有帮助。三元运算符可以节省一些编写时间,但并不一定更清晰。
或者,一些数学知识可以帮助您:
function level(d) {
    var diff = value(d) - median;
    return 2 + (diff > 0 ? -.5 : .5) * (Math.abs(diff) > stdev ? 3 : 1);
}

如果value(d) === median-stdev,则结果为3而不是4。请参考@6502的答案以避免这种情况。


2

如果我们要寻求简短、有创意的解决方案...

那么在IT技术方面,我们可以考虑以下几点:

var level = function(d){
    d = value(d);
    return +(d<=median+stdev)+ +(d<=median)+ +(d<=median-stdev) + 1
}

我觉得这个解决方案相当“有创意”。 - A. Wolff

1
这并不会移除if/else结构,但可以让代码更加简洁:
var level = function (d) {
    var delta = value(d) - median;
    if (delta > stdev) {
        return 1;
    } else if (delta > 0) {
        return 2;
    } else if (delta > -stdev) {
        return 3;
    } else {
        return 4;
    }
 };

它还有一个额外的好处,即只调用value(d)一次。

1

另一种选择——忽略在这种情况下数学的有用性——是完全放弃if语句。对于简单的情况,我更喜欢这种方法而不是使用三元运算符。我倾向于认为这比拥有多个if/else结构更易读(因为我熟悉JavaScript的逻辑运算符)。但我完全可以理解对于那些正在学习或者在奇怪和陌生的语言中编码的人来说,这样看起来可能很奇怪,例如1 && 3 === TRUE而不是3

var level = function (d) {
  d = value(d);
  return ((d > median + stdev) && 1) 
      || ((d > median)         && 2) 
      || ((d > median - stdev) && 3)
      || 4
  ;
}

进一步的可能优化 - 仅适用于此问题 - 是从比较中删除median,但这很可能会影响可读性:
var level = function (d) {
  d = value(d) - median;
  return ((d > + stdev) && 1) 
      || ((d > 0)       && 2) 
      || ((d > - stdev) && 3)
      || 4
  ;
}

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