Logo
Section 1 / 15

逻辑表达式

本课介绍了一种用于条件语句的特定类型表达式。

导入

逻辑表达式

本课介绍了一种用于条件语句的特定类型表达式。

表达式

程序实际执行的工作是通过表达式完成的。任何能够产生值或产生副作用的程序部分都称为表达式。这个定义非常宽泛,因为即使是常量值如 42 和字符串如 "hello" 也属于表达式,因为它们分别产生常量值 42 和 "hello"。

注意: 像上面这样的常量值可以独立于变量使用。

像 writeln 这样的函数也是表达式,因为它们也会为输出流产生值。在 writeln 的情况下,输出流通过在其上放置字符而被修改。我们之前编写的程序中的另一个例子是赋值操作,它同样会修改其左侧的变量。

表达式可以作为其他表达式的一部分。这使得我们可以从简单的表达式组合出更复杂的表达式。例如,假设存在一个名为 currentTemperature 的函数,用于返回当前气温值,那么该函数产生的值可直接用于 writeln 表达式中:

1writeln("现在气温是 ", currentTemperature(), " 度。");

这一行代码包含四个表达式:

  1. "现在气温是 "
  2. currentTemperature()
  3. " 度。"
  4. 使用其他三个表达式的 writeln() 表达式。

在本课中,我们将重点讲解条件语句中使用的特定类型表达式。

在继续深入之前,让我们重新回顾一下在给变量赋值一课中讨论过的赋值运算符,这次特别强调其左右两侧的两个表达式:赋值运算符 = 将右侧表达式的值赋给左侧表达式(例如变量)。

1temperature = 23 // temperature 的值变为 23

逻辑表达式

逻辑表达式用于布尔运算。它们使计算机程序能够做出决策,例如“如果答案是‘是’,我就保存文件”。

逻辑表达式只能有两个值之一:

  • false,表示假
  • true,表示真

让我们看几个使用 writeln 表达式的例子。如果某行末尾打印了 true,表示该行输出的内容为 true。类似地,false 表示该行内容为假。例如,如果程序的输出如下:

1有咖啡:true

则意味着“有咖啡”。同样地,

1有咖啡:false

则意味着“没有咖啡”。

注意: 我使用“... 是 ...: false”这种结构来表示“不是”或“为假”。

逻辑表达式广泛应用于条件语句、循环和函数参数中。理解它们的工作原理至关重要。幸运的是,逻辑表达式很容易解释和使用。

逻辑运算符

逻辑表达式中使用的逻辑运算符包括:

== 运算符

== 运算符回答的问题是:“是否相等?” 它比较其左右两侧的两个表达式,若两者相等则产生 true,否则产生 false。根据定义,== 产生的值是一个逻辑表达式。

举个例子,假设我们有以下两个变量:

1int daysInWeek = 7; 2int monthsInYear = 12;

下面的代码包含两个使用这些值的逻辑表达式:

1import std.stdio; 2 3int main() { 4 int daysInWeek = 7; 5 int monthsInYear = 12; 6 7 writeln(daysInWeek == 7); // true 8 writeln(monthsInYear == 11); // false 9 return 0; 10}

!= 运算符

!= 运算符回答的问题是:“是否不相等?” 它比较两侧的表达式,并产生与 == 相反的结果。

1import std.stdio; 2 3int main() { 4 int daysInWeek = 7; 5 int monthsInYear = 12; 6 7 writeln(daysInWeek != 7); // false 8 writeln(daysInWeek == 7); // true(与 != 相反) 9 writeln(monthsInYear != 11); // true 10 return 0; 11}

|| 运算符

|| 运算符表示“或”,只要任一逻辑表达式为真,就产生 true。
如果左侧表达式的值为真,则直接产生 true,而无需查看右侧表达式。如果左侧为假,则结果等于右侧表达式的值。该运算符类似于英语中的“或”:只要左边、右边或两者都为真,结果就是真。

下表列出了该运算符两边所有可能的取值及其结果:

|| 运算符的结果
|| 运算符的结果
1import std.stdio; 2 3void main() { 4 // false 表示“无”,true 表示“有” 5 6 bool existsCoffee = false; 7 bool existsTea = true; 8 9 writeln("有热饮:", existsCoffee || existsTea); 10}

由于两个表达式中至少有一个为真,上述逻辑表达式的结果为真。

&& 运算符

&& 运算符表示“和”,只有当两个表达式都为真时,才产生 true。
如果左侧表达式的值为假,则直接产生 false,而不去检查右侧表达式。如果左侧为真,则结果等于右侧表达式的值。该运算符类似于英语中的“和”:只有当左值和右值都为真时,结果才是真。

&& 运算符的结果
&& 运算符的结果

注意: || 和 && 运算符可能不会评估右侧表达式,这种特性称为短路行为。我们将在后续章节中学习的三元运算符 ?: 也有类似特性,它从不评估其三个表达式中的一个。其余所有运算符都会评估并使用其所有表达式。

^ 运算符

^ 运算符回答的问题是:“只有一个为真,但不能两个都为真?” 该运算符仅在其中一个表达式为真时产生 true,如果两个都为真或都为假,则产生 false。

注意: 实际上,这个运算符并非逻辑运算符,而是算术运算符。它仅在两个表达式均为布尔类型时才表现出逻辑运算的行为。

^ 运算符的结果
^ 运算符的结果

例如,当用户的朋友中只有一人到场时,他们才会玩国际象棋,这种逻辑可以这样编码:

1import std.stdio; 2 3int main() { 4 bool jimShowedUp = false; 5 bool bobShowedUp = true; 6 7 writeln("我会下棋:", jimShowedUp ^ bobShowedUp); 8 return 0; 9}

< 运算符

< 运算符回答的问题是:“是否小于?”(或“在排序顺序中是否靠前?”)

1import std.stdio; 2 3int main() { 4 int theirScore = 3; 5 int ourScore = 4; 6 7 writeln("我们赢了:", theirScore < ourScore); 8 return 0; 9}

> 运算符

> 运算符回答的问题是:“是否大于?”(或“在排序顺序中是否靠后?”)

1import std.stdio; 2 3int main() { 4 int theirScore = 3; 5 int ourScore = 4; 6 7 writeln("他们赢了:", theirScore > ourScore); 8 return 0; 9}

<= 运算符

<= 运算符回答的问题是:“是否小于或等于?”(或“在排序顺序中是否靠前或相同?”)该运算符是 > 运算符的相反。

1import std.stdio; 2 3int main() { 4 int theirScore = 3; 5 int ourScore = 4; 6 7 writeln("我们没有输:", theirScore <= ourScore); 8 return 0; 9}

>= 运算符

>= 运算符回答的问题是:“是否大于或等于?”(或“在排序顺序中是否靠后或相同?”)该运算符是 < 运算符的相反。

1import std.stdio; 2 3int main() { 4 int theirScore = 4; 5 int ourScore = 4; 6 7 writeln("我们没有赢:", theirScore >= ourScore); 8 return 0; 9}

! 运算符

! 运算符表示“相反”。与其他逻辑运算符不同,它只接受一个表达式,当该表达式为假时产生 true,当该表达式为真时产生 false。

1import std.stdio; 2 3int main() { 4 bool existsBicycle = true; 5 6 writeln("我将步行:", !existsBicycle); 7 return 0; 8}

表达式的分组

可以通过使用括号来指定表达式的求值顺序。当括号内的表达式出现在更复杂的表达式中时,括号内的表达式会先被求值,然后才能用于外层表达式。例如,表达式“如果有咖啡或茶,并且还有饼干或司康饼;那么我就开心”可以这样编写:

1import std.stdio; 2 3int main() { 4 bool existsCoffee = false; 5 bool existsTea = true; 6 bool existsCookie = false; 7 bool existsScone = true; 8 9 writeln("我开心:", 10 (existsCoffee || existsTea) && (existsCookie || existsScone)); 11 return 0; 12}

如果未对子表达式进行括号分组,表达式将按照 D 语言的运算符优先级规则进行求值(该规则继承自 C 语言)。由于在这些规则中 && 的优先级高于 ||,因此不加括号地书写表达式将无法按预期方式求值:

1import std.stdio; 2 3int main() { 4 bool existsCoffee = false; 5 bool existsTea = true; 6 bool existsCookie = false; 7 bool existsScone = true; 8 9 writeln("我开心:", 10 existsCoffee || existsTea && existsCookie || existsScone); 11 return 0; 12}

此时 && 运算符会优先计算,整个表达式等价于以下形式:

1writeln("我开心:", 2existsCoffee || (existsTea && existsCookie) || existsScone);

这具有完全不同的含义:“如果有咖啡,或者有茶和饼干,或者有司康饼;那么我就开心”。

1import std.stdio; 2 3int main() { 4 bool existsCoffee = false; 5 bool existsTea = true; 6 bool existsCookie = false; 7 bool existsScone = true; 8 9 writeln("我开心:", 10 existsCoffee || (existsTea && existsCookie) || existsScone); 11 return 0; 12}

读取布尔输入

上述所有布尔值都会自动显示为 false 或 true。同时,readf() 会自动将字符串 false 和 true 转换为布尔值 false 和 true。它接受大小写字母的任意组合。例如,'False' 和 'FALSE' 会被转换为 false,而 'True' 和 'TRUE' 会被转换为 true。


在下一课中,你将遇到一个与逻辑表达式相关的编程挑战。

变量和用户输入

挑战:修复代码