【问题标题】:Is it better to add a conditional inside or outside of a for loop for cleaner code?在 for 循环内部或外部添加条件以获得更清晰的代码更好吗?
【发布时间】:2020-01-26 20:48:37
【问题描述】:

所以这听起来可能很简单,但我有一个内部有 for 循环的方法,在 forloop 内部,方法 createprints 需要从 getParameters 获取的“参数”映射,现在有两种类型的报告,一种有一个通用的参数集,而另一个具有该通用集和自己的一组参数。

我有两个选择:

有两个getparameters方法,一个是通用的,另一个是用于rp2但也调用通用参数方法。如果我这样做,那么在 for 循环之前添加条件是有意义的:

    theMethod(){

        if (rp1){
            for loop{
                createPrints(getgenParameters())
                do general forloop stuff 
           }
        }else{
              for loop{
                createPrints(getParameters())
                do general forloop stuff
                }
        }
    }

这样,它只检查一次要调用的参数方法,而不是在循环中使用 if 语句,以便检查每次迭代(这很糟糕,因为报告类型在整个循环中永远不会改变)但是这样,重复for 循环看起来很丑,而且根本不干净,有没有更干净的方法来设计它?

另一种选择是将布尔值传递给 get parameters 方法,基本上您可以检查它是哪种类型的报告,并根据您创建的地图,但是这也会在每次迭代时添加一个条件。

从性能的角度来看,将条件放在循环之外是有意义的,这样它就不会在每次迭代时进行冗余检查,但它看起来并不干净,而且我的老板真的很关心代码看起来有多干净,他不喜欢我使用一个 if else 代码块,而不是使用三元运算符,因为三元只使用一行(我认为性能仍然相同吗?)。

忘了说我用的是java,我不能给变量分配函数或使用回调 在方法内部,在 for 循环之前有一个 if else 代码块,类似于

String aVariable;

if(condition){
aVariable= value1;
}else{
aVariable =value2;
}

所以我最初只想创建一个像 isreport1 这样的布尔变量,并且在 if/else 代码块内部也分配值,因为它使用相同的条件。然后如前所述传入参数但是,我的老板又说不要在参数中使用布尔值,所以这种情况我不应该在这里做吗?

【问题讨论】:

  • 你忘了if(我猜)。
  • 三元组不一定if...else 更有效,而且它们往往会混淆代码。如果您的代码最终会打印某些内容(createPrints()),无论如何这不太可能成为您的瓶颈。 “哪个更快?”的答案是总是配置它并查看。
  • 对不起代码不是我的意思,我通常不使用伪代码,现在修复它
  • 是的,我真的不认为三元组更快,这就是我感到困惑的原因,他说它看起来更干净,我觉得这很自以为是,但这不是我关心的问题,我是否应该在里面添加条件逻辑一个通用循环,或者像我上面显示的那样,它运行的步骤更少,因为它只有一个条件而不是每次迭代,但它看起来也不干净,有没有办法让它看起来更干净?我还被告知不要将布尔值传递给方法,因为你可以使用它的唯一方法是 if else,所以它不是一个很好的做法。
  • 关于每次迭代时在循环内执行if 的性能,尽管在循环期间条件没有改变:这取决于。在编译为机器代码的语言中(无论是在编译时,还是在运行时使用 JIT),CPU 都会进行分支预测并确定每次都选择了相同的分支,因此性能影响可能可以忽略不计。在解释型语言中,开销可能更大,因为 CPU 在解释器中预测分支,而不是直接在您的代码中预测。 en.wikipedia.org/wiki/Branch_prediction

标签: java algorithm performance design-patterns


【解决方案1】:

分支预测是 CPU 的属性,而不是编译器的属性。所有现代 CPU 都有它,不用担心。

大多数编译器都可以从循环中拉出一个常量条件,所以不要再担心了。 Java 也这样做。详细信息:javac 编译器不这样做,它的工作是将源代码转换为字节码,仅此而已。但在运行时,字节码的时间关键部分会在机器中编译,并且会发生许多优化。

忘了说我用的是java,我不能给变量分配函数或使用回调。

你可以。它是通过匿名类实现的,从 Java 8 开始,就有了 lambdas。当然值得学习,但与您的情况无关。

我会选择的

       for loop{
            createPrints(rp1 ? getgenParameters() : getParameters())
            do general forloop stuff 
       }

因为这是最短且最干净的方式。

有很多替代方法,例如定义Parameters getSomeParameters(boolean rp1),您可以使用上述三元组中的“提取方法”轻松创建。

从性能的角度来看,将条件放在循环之外是有意义的,这样它就不会在每次迭代时进行冗余检查,但它看起来并不干净,我的老板真的很关心代码看起来有多干净

从性能的角度来看,这一切都无关紧要。编译器非常聪明,知道大量的优化。只需用简短的方法编写干净的代码,这样它就可以正常工作。

他不喜欢我使用 if else 代码块而不是使用三元运算符,因为三元只使用一行

一个简单的三元单行代码使代码更容易理解,所以你应该去做(复杂的三元可能很难掌握,但这里不是这样)。

(我认为性能还是一样不是吗?)。

当然。请注意

  • 大多数程序部分与性能无关(帕累托原理)
  • 低级优化很少重要(编译器更了解它们)
  • 使用正确数据结构的干净代码很重要

【讨论】:

  • 感谢您的解释,我实际上并不知道分支预测,有趣的是他们没有在数据结构和算法课上教我们,所以我只是在考虑步数。但是,是的,我想我现在关心的是什么构成了干净的代码。实际上,我确实按照您一开始的方式进行了操作,但我不确定这是否干净,即使用三元组来选择要使用的方法。我在 if else 中分配了 3 个变量,但我被告知要为它们使用三元组。我曾认为最好只有一个条件并将其用于所有 3
  • 当你说复杂的三元组时,你的意思是像有多个 else if 或者什么构成了一个复杂的三元组,所以我应该求助于一个常规的 if else 块
  • @xeu 我敢打赌,他们也没有教你多标量架构、寄存器重命名、精确异常、缓存一致性协议以及现代 CPU 的大量其他特性。我们免费获得所有这些,而且它很简单……大多数程序员从未听说过它,他们也不需要它。 +++ 干净的代码 - 这是非常主观的,但请考虑两件事:是否容易更改?是否容易理解(想象一个新程序员非常了解该语言但不了解您的代码)。
  • @xeu 复杂的三元 - 这也是主观的。对我来说,像a ? b : c ? d : e 这样的东西就在边缘。每当任何表达式变得长且多行时,都应考虑提取局部变量和/或方法。
  • 嗯,事情就是这样,这是主观的。就像拥有一个 if/else 代码块对我来说更有意义,并且如果所有分配都依赖于相同的条件,我会在那里使用预先初始化的变量进行所有分配,而不是创建 3 个单独的三元运算符只是因为它们在技术上是一行每个。
【解决方案2】:

大多数关于编写某段代码的方法是否比另一种更有效的“辩论”都没有抓住重点。

JIT 编译器在后台对您的代码执行大量优化。它(或者更准确地说是编写它的人)比你更了解如何优化。他们将拥有进行广泛的基准测试和(机器级)代码演练等的优势,以找出最佳的优化方法。

编译器还可以比人类更可靠地进行优化。它的开发者也在不断改进它。

并且编译器不需要支付薪水来优化代码。是的。您花在优化上的每一个小时都是您可以花在其他事情上的时间。

所以优化时最适合您的策略如下:

  1. 首先实现代码。编码时花在优化上的时间可能被浪费了。这是经典的“过早优化”行为。在这个阶段,你的直觉可能不足以做出正确的决定。 (很少有人真正足够聪明......)
  2. 接下来让代码工作并通过测试。优化有缺陷的代码没有抓住重点。
  3. 设定一些现实的、可量化的绩效目标。 (“尽可能快”既不现实也不可量化)。您的代码只需要足够快即可满足业务需求。如果它已经足够快,那么您进一步优化它就是在浪费开发人员的时间。
  4. 创建一个现实的基准,以便您可以衡量您的应用程序的性能。如果您不能(或不)进行衡量,您将不知道自己是否达到了目标,或者某个特定的优化尝试是否改善了情况。
  5. 使用分析来确定代码的哪些部分值得努力优化。分析会告诉您花费大部分时间的热点。专注于那里……而不是只偶尔执行的东西。这就是 80-20 规则的用武之地。

我会和我的老板谈谈这件事。如果老板不同意在性能/优化方面有条不紊和科学的想法,那就不要抗拒。就按老板说的做。 (但是,如果您因在不必要的优化上浪费时间而错过最后期限而受到指责,那么当您愿意推迟时。)

软件工程有两种效率:

  • 程序的效率
  • 程序员的效率

这两种效率是冲突的。

【讨论】:

  • 谢谢,这在性能方面很有意义。无论如何,我真的不认为我正在处理的这部分会对性能产生很大影响,这是在实现完成之后,我被告知将方法连接在一起并编写更干净的代码,我现在意识到这可能是更多关于此类情况下的可读性或行数?你认为我应该寻找什么以及什么构成“干净的代码”
  • 干净的代码是见仁见智。我认为您应该请告诉您清理代码的人给您一些指示。 (如果您不了解老板真正想要什么,那么花大量时间重写代码将是一个坏主意。询问。)另外,请查看x-team.com/blog/principles-clean-code 以获取一些想法
  • 是的,我意识到了这一点,我已经问了他很多问题,但正如你提到的,很多时间不应该花在这样的事情上,我认为继续问下去看起来不太好或讨论简单的设计问题。您提到编译器将完成大部分优化,我们不应该过多关注这一点,但我们不应该像编译器不会优化代码一样编写代码吗?就像在这种情况下在循环内部添加一个条件并且编译器优化执行在 for 循环之外添加它一样?依赖编译器来完成工作是一种好习惯吗?
  • 有时对我的老板来说干净代码的意见听起来像是优化问题。例如,最好不要在方法中将布尔值作为参数传递是真的吗?例如,我有一个 sql 查询所需的语言环境参数,以生成所述语言环境语言的 rpt2,而 rp1 不需要查询中的语言环境。我想在函数中传入语言环境布尔值,并在执行查询之前先检查它是否为 rp2,他告诉我改为调用查询以在生成 BOTH 报告的方法之前获取语言中的标签,如果rp1.
【解决方案3】:

这个怎么样?

theMethod(getParametersCallback){

        for loop {
            createPrints(getgenParametersCallback)
            do general forloop stuff 
       }
}

. . .  . .

if (rp1){
    theMethod(getgenParameters)
}
else {
    theMethod(getParameters)
}

【讨论】:

  • 我在用java,我不认为有这样的回调?
  • @xue。有多种方法可以在 Java 中实现回调。例如,lambdas 或匿名类都可以用于此。
  • @xeu 好吧,#java 不存在。而且毫无疑问,java 有回调。geeksforgeeks.org/asynchronous-synchronous-callbacks-java
  • 当然,但是我觉得像这样简单的事情使用回调并没有简化代码
  • @xeu “简化代码”是一个广泛的主题。你在某些情况下做事。例如,您可能只有一个类class Printer { Printer (IParamProvider pp, IPrintCreator pc) {} TheMethod () { pc.Print(pp); } }。多么简单啊?现在我们讲依赖注入和其他面向对象的概念
猜你喜欢
  • 1970-01-01
  • 2015-09-14
  • 1970-01-01
  • 2010-12-25
  • 1970-01-01
  • 1970-01-01
  • 2017-07-23
  • 1970-01-01
  • 2022-01-23
相关资源
最近更新 更多