欢迎光临散文网 会员登陆 & 注册

第 11 讲:运算符(三):比较、逻辑和条件运算符

2021-03-26 09:22 作者:SunnieShine  | 我要投稿

下面我们介绍一下比较运算符、逻辑运算符和条件运算符。这三种运算符需要一起讲,因为它们之间是存在关联和关系的。

Part 1 六种比较运算符

先别被标题吓死了。六种比较运算符实际上就是我们经常在数学上使用的大于、大于等于、小于、小于等于、等于和不等于这六种比较符号。在 C# 里,和 C 语言一致,分别写成 >>=<<===!=。特别需要说明的是,等号需要用两个等号表达,因为单个等号已经用来表达赋值过程了。为了避免赋值过程和比较过程使用同一个符号产生语义冲突,比较相等就多用了一次等号符号。

实际上也没什么好说的。它实际上就是在比较数据的大小。将比较结果(条件是否正确)以一个 bool 结果反馈和赋值给左侧变量。

Part 2 八种逻辑运算符

C# 里有四种逻辑运算,分别是且(&&)、或(||)、贪婪且(&)、贪婪或(|)、且元(false)、或元(true)、异或(^)和逻辑取反(!)。我相信你肯定只知道逻辑且、或、非(逻辑取反),最多多一个逻辑异或运算。其它四个估计你完全不知道。这也没关系,我们慢慢来梳理它们。

2-1 逻辑且、或运算

我们使用 && 连接两个 bool 类型的变量,用来表达和表示两个条件必须都成立的时候,才成立。

这个 condition 的值是 true 还是 false 呢?显然是 true,因为两个条件 ab 全都是正确的表达式,因此相当于在算 true && true,那当然结果就是 true 了。

||&& 是对称的运算。只有当连接起来的两个条件都不成立的时候,才不成立。其它情况都成立。

显然,两个都是 true 的结果。只有两个都是 false 的时候,结果才是 false。因此这个例子的 condition 结果依旧是 true

2-2 贪婪逻辑运算以及短路现象

要说清楚贪婪逻辑运算,必须要先说一下逻辑且和逻辑或本身存在的一种短路现象,它分逻辑且和逻辑或两种运算来解释:

  • 逻辑且运算(&&):由于两个条件里,有一个为 false 的时候,就已经可以确定表达式结果,因此如果当左侧结果就是 false 的时候,右侧结果不论是普通表达式,还是带有运算过程的表达式,都不会被得到执行;

  • 逻辑或运算(||):由于两个条件里,有一个为 true 的时候,就已经可以确定表达式结果,因此如果当左侧结果就是 true 的时候,右侧结果不论是普通表达式,还是带有运算过程的表达式,都不会被得到执行。

可以从内容里看出,它们的差距就在 truefalse 这个词不一样。下面我们来说一下这个到底是什么意思。

可以看到,其实两个例子的结果分别是 0, 4, true0, 3, true。非贪婪运算在计算 --a <= 0 的时候,发现 -- 是前缀运算,因此先把 a 减去一个单位,使得 1 变为 0;然后 --a 的结果就是此时 a 的数值(即 0);最后,--a <= 0 就一定是成立的,因此整个表达式的结果是 true

因为使用的是逻辑或运算,因此左侧条件已经可以确定整个表达式 --a <= 0 || --b <= 0 的结果了,那么,--b <= 0 就不必计算了,即使 --b 会影响 b 的结果,此时的 b 照样不会减去 1 个单位,因此 a 变成 0,b 没有任何改动,且最终的结果是 true

贪婪逻辑或运算说白了就是没有短路现象的逻辑或运算。不管左边是不是能确定表达式结果,右侧的表达式依旧需要运算。那么,结果自然就是 0, 3, true 了(虽然我们知道,就算 b 变成 3,--b <= 0 也是 false 的;但是因为 true 或一个不管什么东西的结果都是 true)。

同样地,逻辑且和贪婪逻辑且运算是一样的道理。

思考一下,这个例子里的结果分别是多少,以及把贪婪逻辑且运算改成逻辑且运算后,结果又是多少。

2-3 逻辑异或运算

异或运算类似于我们逻辑上理解的“要么……要么……”。如果别人告诉你,要么苹果要么香蕉的时候,如果你都选择了,或者都不要的话,这两种情况都是不成立的。因为要么表示里面的东西你必须选,但是只能选一个。异或运算就是这么一个逻辑。

由于 a > 10 的结果是 false,而 b >= 3 的结果是 true。按照逻辑异或运算的规则,因为有且仅有一个表达式结果为 true,因此 condition 的结果就是 false ^ true = true

最后,我们还剩下逻辑元运算两个(truefalse)。这一点我们先放一边,我们先讲条件运算。

2-4 逻辑取反运算

逻辑取反运算比起前面的就要简单很多了。逻辑取反就是把对改成错、把错改成对的过程。

如果 a > b 条件为真(即 true 的话),那么 condition 的结果就为 false

请注意,! 运算符仅用来表达一个 bool 结果的取反,因此必须要在表达式整体上打括号后,然后前面追加 !。如果写成 !a > b 的话,C# 会产生一个编译器错误,告诉你 !a 是无法取反的,因为 a 是一个数。

Part 3 条件运算符

条件运算符(Conditional Operator)是一个需要三个表达式才能操作的运算符,它用 ?: 作为分隔,问号的前面写条件(一个 bool 类型的变量),然后 ?: 中间写这个条件成立的时候的数值,: 后写条件不成立的时候的数值。两个数值必须是同一种数据类型的。

举个例子:

这表示,当 a > 10 条件成立的时候,我们认为 30 就是 c 的数值,否则 c 为 40。

Part 4 逻辑元运算

坐稳了。这一节的难度有点大,而且很考察各位的逻辑推理能力。千万不要掉以轻心。

逻辑元运算(Meta Logical Operator)一共有两个(truefalse)。不要以为 truefalse 只能表示一个条件为真和假,它还有别的意思:当成一个运算符,记作 true(表达式)false(表达式)

逻辑元运算将 &&&,还有 ||| 关联起来。公式大概是这样的:

  • a && b 等价于 false(a) ? a : a & b

  • a || b 等价于 true(a) ? a : a | b

比如 a && b。我们先运算 false(a) 的结果,如果这个结果是 true,我们就直接把 a 作为 a && b 的结果;否则,就取 a & b 整个表达式的结果。

不好理解?行,我们拿驾车的例子给大家介绍一下逻辑元运算。假设我们开着一辆车向前行驶。遇到红绿灯路口需要过马路。那么,如果我们将车辆油箱里的油还剩下多少分成三种状态:

  • 红色:没油了;

  • 黄色:有一点油,但是要提出警告,因为可能过不了马路;

  • 绿色:有充足的油前进。

我们把红绿灯的状态也表示成三种情况:

  • 红色:红灯无法前进;

  • 黄色:黄灯;

  • 绿色:绿灯可以前进。

那么,假设我们把“带有红色、黄色、绿色”这三种状态的逻辑称为“状态逻辑”。那么,状态逻辑的运算应该如何呢?

  • 如果两个条件里只要有一个是红色的话,我们就断定结果状态一定是红色的;

  • 如果两个条件里两个都是黄色的话,我们也认为最终状态也是红色的;

  • 如果两个条件有一个黄色的话,我们就认为结果状态是黄色的;

  • 如果前面都不成立,则现在只剩下绿色了,因此我们认为其它情况下的结果状态都是绿色的。

那么,我们可以具象化逻辑:定义一个“状态逻辑”的变量 canGo,表示是否可以前行。它取决于“油箱是否有油”和“前面红绿灯是否可以允许前进”两个因素,因此我们需要把这两个条件用逻辑且连接起来:

其中,fuelStatus 是油箱有没有油的状态(有油,有油但是少、没有油);signalStatus 是红绿灯是否允许我们前进的状态(红灯、黄灯和绿灯)。

那么问题来了。这个运算到底怎么计算呢?这个时候我们就需要设计 truefalse 这两个逻辑元运算的过程了。

  • true(状态):状态是绿色的时候,结果为 true,否则为 false

  • false(状态):状态是黄色或红色的时候,结果为 true,否则为 false

很好。我们最后带入公式,看看是否逻辑正常:fuelStatus && signalStatus 用逻辑元运算来表达的话,可以展开成 false(fuelStatus) ? fuelStatus : fuelStatus & signalStatus

这个逻辑就很好理解了。如果油箱状态是红色(或黄色)的时候,我们就可以直接认为 canGo 直接是红色(或黄色)了:因为既然油箱都没油了,那我们自然就不可能前进(这一点就是表达式里体现到的短路现象)。但是,如果油箱的油还多的话,我们这个时候就肯定要看红绿灯是否允许我们前行,因此,我们还要看 signalStatus 的结果才行。

那么,为什么不是直接看 signalStatus 的状态作为结果,而是 fuelStatus & signalStatus 呢?因为,我们不能仅通过信号灯的状态就确定我们是否可以继续通行,因此需要看的是油箱和信号灯两个状态才可以断言结果。你换个角度想一想 bool&&&,就可以想通这个道理了。

这就称为逻辑元运算。逻辑元运算决定了条件是否成立的底层逻辑,而 &&|| 可以认为是用贪婪运算和逻辑元运算共同构成的一个复杂表达式。

逻辑元运算是唯一一个无法直接使用的运算符;换句话说,你无法使用代码书写的方式来使用这个运算符。比如说你这么写代码:false(condition),这个写法就是不行的。它的出现实际上是为了辅助 & 运算符(贪婪逻辑且运算)和 | 运算符(贪婪逻辑或运算)提供帮助,构成 &&|| 的中间运算过程。正是因为它是中间运算过程,因此难度比套用公式还要难理解一些。

Part 5 总结

本节内容难度可能有点大,考察各位对逻辑的理解。比较运算其实没有什么可以说的,主要就是逻辑运算比较不好理解,短路现象、元运算等等。


第 11 讲:运算符(三):比较、逻辑和条件运算符的评论 (共 条)

分享到微博请遵守国家法律