高健:If-else,真的很Low吗?

高健:If-else,真的很Low吗?

一、入门练习题


先看个练习题,根据学生的成绩进行评级。


成绩区间 评级
0-59 挂科
60-69 及格
70-79 中等
80-89 良好
90-99 优秀
100 满分
其他分数 分数不合法


这道题的逻辑,非常简单,初学代码一周的人,都能写出来。


但是我发现,有很多工作多年的人,仍然会这上面翻车,把简单的问题复杂化了。


在我的一个回答下,很多人在评论区里,对我在代码中使用大量的if-else表示不满,觉得if-else很Low,但是说不出个所以然来。



因此,我认为有必要好好的做一次详细的讲解和性能测试,看看在多种写法里,哪种写法最好,哪种写法最糟糕。


面对多条件判断,通常会有五种写法。

  1. 多if 语句
  2. if-elif语句
  3. 优化版本多if语句
  4. 表驱动
  5. Match case

这五种写法,都能实现其功能。但是一般的开发,很难说出来各种写法的优缺点,无法在实际开发中灵活运用。

因此,我特地把这五种写法都写出来,每种执行1000万次,

主要从可读性和执行时间两个维度,进行评价。

1.多if语句写法


多if语句


  1. 使用多个if语句会降低程序性能,即使条件不成立,也需要执行每个if语句,导致效率低下。此外,程序员容易遗漏某些条件,如在写这段代码时,我就未检查大于100或小于0的情况。
  2. 多个if语句会导致代码逻辑混乱,难以理解。很容易出现一个表达式满足多个条件语句的情况。
  3. 在互斥条件下,多个if语句的性能较差,因为每个条件都需要进行评估。
  4. 因此,不推荐使用多个if语句,因为它们不仅可读性差,而且执行时间较长。

2.If-elif语句

为了解决互斥条件问题,就引入了if-elif语句。


if elif 语句


if-elif语句的性能表现优异,因为它只会执行第一个满足条件的分支,从而避免了多个if语句带来的冗余判断,大大提升了程序的执行速度。同时,也降低了程序员的认知负担,避免因为疏忽导致bug的出现。

在Python中,当遇到大量条件或对性能敏感,那么使用if-elif语句是一个较好的实践。

在可读性上和性能上,if-elif的表现都很不错,可以满足绝大多数使用场景。

3.优化版本多if语句

前面说到多if语句的问题,从性能上,我们可以对其进行优化,每个判断条件后,都加个return语句,使其在满足一个条件时,就退出函数。


多if语句优化版本


在改进之后,因为及时return,看起来很像Switch-case break,其性能也超过了前两种写法,不过该写法的可读性依旧不如if-elif。如果习惯用多if语句,可以及时return的方法进行优化。

4.表驱动

当if语句过多时,考虑使用表驱动进行优化。

表驱动通常更快,更简洁,因为表的查找可以在常数时间内完成,而if-else语句需要逐个评估每个条件,直到找到为真的条件。

因此,在经典书籍中,通常建议使用表驱动来优化if-else。

表驱动不仅可以消除大量if-else语句,在复杂情况下性能更优,而且通过将数据和逻辑分离,可以更容易地修改代码,而不必修改代码本身,从而使代码更具可扩展性。

选择表驱动还是if-else语句,这取决于代码的性能、可读性、可维护性之间的权衡。

在实际开发中,需要根据实际情况灵活应用。表驱动在分段函数的情况下性能最差。对于简单条件,使用if-else语句可能更简单并具有更高的可读性。此外,创建和维护表可能需要额外的开销,在某些情况下可能不值得性能提升。


表驱动


5. Match case

以前,在Python里是没有“Switch case”语法。Python 在 3.10版本后 , “Switch case” 千呼万唤始出来。


match_case


按照语法来说,严格点说,应该是Match case,和其他静态语言不一样的是,Python保留了一贯简洁的风格,Match case里默认标识break,如果不习惯,可以增加return进行标识和优化。


match_case2


可以看到,在Match case里,仍需要做判断,性能接近if elif, 但是可读性,远远不如if elif。当然,如果你是Match case 爱好者,当我没说;如果和你交接的同事不是Match case爱好者,TA 大概率会疯狂吐槽你。

在开发中,通常不会用Switch case,除了可读性差。还因为在实际业务中,条件语句通常较为复杂,不仅仅涉及简单的等于或不等于关系,还包括范围判断和多重逻辑判断等。此时,if else 语句更为灵活,通过多个 if else 语句可以实现更加复杂的条件判断。

相比之下,switch case 语句仅能对单个变量进行等值比较,无法处理复杂的条件判断。此外,if else 语句代码的可读性更强,易于理解和维护。因此,if else 语句通常被视为更优的选择。

二、运行结果测试

我写了两版本,第一版是用Python写的,跑了一千万次,执行速度,优化版的多if语句执行最快,传统的if elif语句紧随其后。而表驱动法,在执行用时最长。


python_跑一千万次


在Python里跑一亿次,可以更清晰的看到优化的if语句和传统if-elif语句执行速度的优势。


python_跑一亿次


第二版,相同的逻辑,我写了C#版,这也体现出编译性语言的优势,比Python这种解释性语言快多了。


在C#里,跑一亿次,执行速度最快的是依旧是优化版的多if语句,传统的if else 和Switch case紧随其后,最慢的依旧是表驱动。这个性能差距,就太大了。


C# 一亿次结果


当上到两亿次的时,性能差距会更明显。


C# 两亿次结果

可以看到,无论是在Python里还是在C#里,在遇到逻辑相对简单的代码时,传统的If……else要远胜过表驱动法。


可读性和维护性,请问在这个业务场景下,if-else if的可读性,真的要比表驱动法的可读性差吗?if...else if的可维护性真的不如表驱动吗?


想学好编程,一定要动手去验证。


三、C#逻辑代码:

1.多If语句


C# 多if语句


2.if-else语句


C# if else if 语句


3.多if语句优化版


多if优化版


4.表驱动


C#表驱动


5. Switch Case


C# Switch case


四、为什么C#会比Python快这么多?

这里再单独解释一下,用Python是因为写的快,用C#是因为程序跑起来快。

C#之所以快,有以下几个原因:

  1. 编译性语言vs 解释性语言:C# 是一种编译语言,它在运行之前会被翻译成机器码,这使得执行速度更快。而Python 是一种解释语言,它会逐行解释和执行代码,因此程序执行时间会更慢。
  2. 静态类型 vs 动态类型:C# 是一种静态类型语言,变量的数据类型在编译时已经确定,这使得内存分配更有效率,执行更快。而 Python 是一种动态类型语言,变量的数据类型在运行时才能确定,因此可能导致更慢的执行时间和效率较低的内存分配。
  3. JIT 优化:C# 使用 Just-In-Time(JIT)编译器在运行时优化代码,这使得执行更有效率、更快速。Python 也有 JIT 优化,例如 PyPy,但它不像 C# 的 JIT 编译器那样被广泛使用。
  4. 低层编程结构:C# 提供了指针和内存管理等低层编程结构,这使得程序的性能可以更加精细地控制。相比之下,Python 没有这些低层结构,因此执行的更慢。

综上几个原因,使得Python在实际开发中,占比相对不高。


五、语法和语言的取舍


新工具的出现,很大程度上,在于弥补旧工具的缺陷以及应对新的业务场景。


语法和语言一样,没有最好的,只有最合适的,根据业务进行权衡和取舍。


初学编程时:

看山是山,看水是水


上手编程一段时间:

看山不是山,看水不是水


踩过编程里大大小小的坑以后:

看山还是山,看水还是水。


六、表驱动最佳实践


放到这道题,表驱动也有优化空间。


第一,可以通过把Map放到方法外边。

第二,通过除法,取出成绩高位,通过该方法查表。把在区间取值,转换根据值取值。


优化表驱动


优化过后,该表驱动的速度,要远远优于未优化的表驱动方法,但还是落后于传统的if-else方法。


跑一亿次后


七、通过数组下标进行取值


通过观察,可以看到,在表驱动里,通过key取value,比如用3,代表挂科,用6代表及格,用10代表满分。


这种方法,在表驱动里最为通用。但在本例里,既然用数字做key,这个key可以用数组的索引来表示,因此可以进一步优化其结构,用List代替Map。


编辑于 2023-04-17 15:39 ・IP 属地中国台湾

文章被以下专栏收录