明天说说德摩根定律在编程中的实际,题目看的很吓人,其实只有有一点点的高中数学常识就能看懂,而且这部分常识把握后能够很快的使用到我的项目中,投资收益比十分高。
如果你感觉我的文章对你有帮忙,在珍藏的过程中,肯定要记得点赞哦,谢谢你,这对我真的很重要????!
一、缘起:一段让人头大的逻辑判断
这两天在重构一些老我的项目,重构过程中遇到了一个让人十分头大的逻辑判断:
if(!((A && B) || C)) {
// do something
} else {
// do something
}
看了这段代码,我人都傻了,从里向外一层一层梳理逻辑时,我的大脑流动是这样的:
短短一行的逻辑判断里,与或非三个运算符都用上了,尤其是最初那个小括号一圈整体取反的操作,我脑子间接炸了。要晓得人脑是很不善于或运算和非运算的,更不要说这些运算组合在一起了。
又花了五分钟尝试从代码上下文中梳理业务逻辑无果后,我从新扫视了这个问题:如果业务上不好解决这个问题,能不能从实践上找到突破口?
方向找对后,我很快就找到了解决方案,那就是离散数学里的德摩根定律(De Morgan’s laws)。
二、什么是德摩根定律
德摩根定律咱们其实很早就接触过了,高中数学的汇合局部就讲过,大学离散数学的汇合运算和布尔代数局部也有所提及。
德摩根定律在离散数学的很多场景里都呈现过,它一共有两个关系:
- 在命题逻辑里,能够这样示意:
$$\neg (P\lor Q)\iff (\neg P)\land (\neg Q)$$
$$\neg (P\land Q)\iff (\neg P)\lor (\neg Q)$$
其中 $\neg$ 示意逻辑非运算符(NOT, !
),$\lor$ 示意逻辑或运算符(OR, ||
),$\land$ 示意逻辑与运算符(AND, &&
)
- 在汇合里能够这样示意:
$$\overline {A\cup B} = \overline {A} \cap \overline {B}$$
$$\overline {A\cap B} = \overline {A} \cup \overline {B}$$
其中 A 和 B 示意汇合,汇合上的横线示意取补集,$\cap$ 示意取交加,$\cup$ 示意取并集。
- 在布尔代数里能够这样示意:
$$\overline {(x \cdot y)} = \overline {x} + \overline {y}$$
$$\overline {(x+y)} = \overline {x} \cdot \overline {y}$$
其中 $\cdot$ 示意布尔积(AND),$+$ 示意布尔和(OR),上划线示意补(NOT)。
- 下面的公式还能够用文字描述:
非($P$ 或 $Q$)等价于(非 $P$)且(非 $Q$)
非($P$ 且 $Q$)等价于(非 $P$)或(非 $Q$)
- 或者用程序员相熟的与或非逻辑运算符示意:
!(P || Q) = !P && !Q
!(P && Q) = !P || !Q
公式曾经摆在这里了,必定能够间接用了,然而用的时候还是不太释怀,最好还是本人验证一下。咱们能够用真值表对 $\neg (P\land Q)\iff (\neg P)\lor (\neg Q)$ 做个直观的验证:
$P$ | $Q$ | $\neg P$ | $\neg Q$ | $(P\land Q)$ | $\neg (P\land Q)$ | $(\neg P)\lor (\neg Q)$ |
---|---|---|---|---|---|---|
0 | 0 | 1 | 1 | 0 | 1 | 1 |
0 | 1 | 1 | 0 | 0 | 1 | 1 |
1 | 0 | 0 | 1 | 0 | 1 | 1 |
1 | 1 | 0 | 0 | 1 | 0 | 0 |
咱们也能够用 Venn 图对 $\overline {A\cap B} = \overline {A} \cup \overline {B}$ 做个可视化验证:
更简单的数学推导我这里就不多说了,感兴趣的同学能够自行搜寻学习。
三、解决问题
具体到我一开始说的那个条件判断上,咱们能够用德摩根定律把原表达式拆开:
!((A && B) || C)
== !(A && B) && !C // 德摩根律
== (!A || !B) && !C // 德摩根律
== (!A && !C) || (!B && !C) // 分配律
用分配律转化后,通过代码的上下文剖析,我发现在这段代码的业务场景中, !A
等价于 C
,所以下面的式子还能够化简:
(!A && !C) || (!B && !C)
== (C && !C) || (!B && !C) // !A 等价于 C(从业务上剖析的,不具备普遍意义)
== false || (!B && !C) // C && !C 必然为 false
== !B && !C
== A && !B // A 等价于 !C(从业务上剖析的)
到这里,我胜利的把原来一段让人脑袋爆炸的判断语句化简为一段直白易懂的表达式,转换后的代码无论是从了解上还是前期保护上都比原来容易很多。
四、化简还有什么招?
与或非三者一起用的场景能够尝试用德摩根定律化简,在其它场景下,还能够利用其它运算律进行转换,比如说后面我就用了分配律。相似于积之和展开式的化简,咱们还能够用卡诺图进行剖析。
例如一个场景试图化简布尔函数的一个积之展开式:$x\overline {y} + \overline {x}y + \overline {x} \overline {y}$ ,就能够用卡诺图进行剖析:
$y$ | $\overline {y}$ | |
---|---|---|
$x$ | 1 | |
$\overline {x}$ | 1 | 1 |
依据图示能够轻易得出最初的化简后果为 $\overline {x} + \overline {y}$。
如果变量超过 4 个,卡诺图就非常复杂了,这时候能够用奎因-莫克拉斯基办法进行化简,限于篇幅也不多介绍了,感兴趣的同学能够自行搜寻。
在此我再多说一句,如果真的呈现用 4 个以上的子状态去决定最终的行为时,问题的要害就不是化简,而是顶层设计上就出了问题了。这时候须要停下来好好思考是不是模型的设计形象水平不够,把过多的外部状态裸露了进去等等,这个得具体业务具体分析,一昧的堆 if else
或加 flag
是解决不了问题的,只会导致代码的腐化最终无奈保护。
最初举荐一波我的微信公众号:卤蛋实验室和我的集体博客:supercodepower.com, 目前专一前端技术,对图形学也有一些渺小钻研,欢送大家来撩~
五、举荐浏览
【答疑解惑】为什么你的 Charles 会抓包失败?
webpack 中那些最易混同的 5 个知识点
React Native 性能优化指南【全网最全,值得珍藏】
发表回复