if-else 语句应该是语句还是表达式呢?这是一个有趣的问题,不同答案会使得编程语言的表现形式很不一样。
表达式,是运行后会产生值的语句,例如 a+b
, a>b
,true
等。简单来说,如果一个语句是表达式,那么这个语句可以写于赋值符号的右边,例如 let a = a > b
。而除了表达式语句以外的语句运行后不会产生值。可以看出,表达式严格而言是语句的一个子集。本文中,我使用『语句』的狭义含义,也就是仅表示语句中不包含表达式语句的部分。
一般在函数式编程语言中,例如 Haskell 中,if-else 是表达式。
let a = if 2 > 1 then 3 else 1
很多支持函数式范式的语言也有类似的表达式,如 Python:
a = 3 if 3 > 1 else 1
# 3 if 3 > 1 else 1 执行后会产生 3 或者 1
但是 Python 中也存在不是表达式的 if-else 语句,如:
if 3 > 1:
a = 3
else:
a = 1
# `a` 直接在 *if-else* 的分支中被赋值的,*if-else* 语句本身并没有产生一个值
在上面三个示例中我们可以看出:
- if-else 如果是表达式,那么 else 是不可缺少的。
- 作为表达式的 if-else 的表达能力更弱。
- if-else 作为表达式设计上更加优雅(个人看法)。
- 在函数式编程语言中if-else 作为表达式是必须的(因为函数式语言是 immutable, 所以 if-else 必须将其执行结果通过表达式值表现出来)。
这就出现了一个设计上的矛盾:if-else 作为表达式或者语句,各有其优缺点。而且如果要引入另一种范式,可能需要增加新的语法,如 Python。Rust 很优雅地解决了这个矛盾。
Rust 中 if-else 语句也是表达式,且其每一个分支必须产生相同类型的值。if-else 之后都必须是大括号包括的代码块,代码块的最后一行即是产生的值。在 else 分支缺省的情况下,else 分支产生的值为 ()
,即 Rust 中的 nothing。
let a = if x > 2 {
x-10
} else {
x + 10
};
如果在代码块的最后一行的表达式加 ;
, 会忽略表达式产生的值,即代码块产生的值为 ()
。也就是说,如果 if
后的代码块产生的值是 ()
时,可以省略 else
。
let mut x = 10;
if x > 10 {
x -= 10;
} // 因为 if 产生的值为 `()`, 结尾不需要加 `;`
从上面的几个例子可以看到,Rust 中 if-else 语句虽然是表达式,却同时可以具有语句的灵活性。通过引入代码块的产生值的概念,;
忽略表达式的值等设计,巧妙地将两种看似不同的设计融合在一起。相比 Python 通过两种语法来支持两种不同范式的设计,Rust 只用了一种语法却可以兼容两种写法,实在是妙啊。
对于这个问题,我们可以思考的更加深入一些。上面提到了在纯函数式编程语言中,因为 immutability ,在执行分支的时候,分支不能改变之前的『变量』的值,分支执行的效果必须通过表达式的返回值表达出来(暂时忽略 IO 产生的效果)。所以 if-else 语句必须作为语句存在于函数式编程语言中。我们可以得出一个武断的结论:如果一门编程语言要支持 immutability, 那么 if-else 必须是表达式。
回到 Rust,考虑下面的功能:现要根据 x
的值是否大于 20
来设定 y
的值为 1
或者 0
。如果允许使用 mut, 实现就是我们很熟悉的样子:
let x = 20;
let mut y = 0;
if x > 20 {
y = 1;
}
但是 如果不允许使用 mut,我们就无法在 if 后面的代码块中去更改 y
的值,而必须通过表达式的值来设置 y
。
let x = 20;
let y = if x > 20 { 1 } else { 0 };
可以看到之所以会出现 if-else 的两种不同设计,其更加根本的原因在于是否是 immutable。而 Rust 恰好想同时支持 mutability 和 immutability,所以其 if-else 语句采用了两种特性可以兼容的设计。
需要指出的是,if-else 作为表达式在 immutable 编程语言中是必须的,在 mutable 编程语言中也是可以存在的。例如 C 语言中有 :?
这种三目运算符,Python 中也有 if-else 作为表达式的语法。这不是必须的,但是有时候能让代码看起来更加简洁。C 语言中的三目运算符一般可以翻译成不需要分支预测的汇编代码(SET 系列指令),从而提高代码效率。
Rust 这个语法设计上的小细节实在是让我感到惊喜。在一门编程语言的设计中,语法和语言支持的特性不是独立存在的,他们常常是相互影响的。两者是形式和内容的关系。内容需要某种形式去表达,反过来,形式也限制了内容的表达。而一门优秀的语言应该充分考虑两者的相互作用。