首页 > 代码库 > 2.2.4 用表达式代替语句

2.2.4 用表达式代替语句

2.2.4 用表达式代替语句

 

在命令式语言中,表达式是简单的、可以计算并产生结果的代码块,因此,方法调用、任何布尔值的使用,或者整数运算,都是表达式;而语句是能够影响程序的状态,但没有任何结果的代码块。不返回任何值的方法调用就是语句,因为它会影响程序的状态,而不管方法做了什么;赋值也会更改状态(通过改变变量的值),但在最简单的情况下,它不会返回任何值。

 

注意

 

在 C# 中,赋值是返回值的,因此,可以写 a = (b = 42);。在这种最简单的形式中(就是我们这里所讨论的),这是就一个语句,把值赋给变量,但不返回任何内容(如 b = 42;)。

 

另一种典型的语句,是在函数中使用 return 返回,或者使用 break 跳出循环,但是,这些结构都没有任何“返回值”;相反,其唯一的目的就是改变程序的状态。return 和 break 改变代码当前语句的执行(return 跳回调用这个方法的代码,break 跳转到循环结束之后)。

如你所知,在函数语言中的状态是由函数的返回值表示,改变状态的唯一方法是返回修改后的值。按照这个逻辑,函数语言中的一切都可解释为有返回值的表达式。这个结论可以很好地用我们前面的示例来说明,计算指定范围内数字的和,下面是代码的原始版本,使用递归,但还不是完全的函数式,因为它连续用了三条语句:

 

int SumNumbers(int from, int to) {

  if(from > to) return 0;

  intsumRest = SumNumbers(from + 1, to);

  returnfrom + sumRest;

}

 

我们可以用 C# 的条件运算符 (?:),把它改成更加函数式的版本。使用这个结构,主体部分可以写成一个表达式,这已经非常接近于函数语言所认为的完全有效代码。在 C# 中,这只能用在这种相对简单的示例代码中,因为,我们不可能在分支条件运算符中声明局部变量。

为了演示函数语言是如何运行的,我们将用一个并不存在的“函数式 C#”,重写这个方法,如清单 2.1 所示,“函数式 C#”唯一非标准的功能,就是它能够在表达式内声明变量。

 

Listing 2.1 Summing numbers in thespecified range in a “functional C#”

 

int SumNumbers(int from, int to) {

  return

    (from> to)    [1]

     ? 0        [2]

     : { var sumRest = SumNumbers(from + 1, to);  | [3]

       from + sumRest; };                    |

}

 

只用表达式写代码,会受到相当多的限制,因为大多数控制流结构(比如条件或循环)都是语句。虽然示例很简单,但它说明,用函数语言可以做什么:

■整个方法体就是一个可以返回值的表达式。因此,在 C# 中,方法体必须以 return 开头;此外,在代码的其它任何地方都不能使用return[1],因为这会导致从表达式的中间跳转到方法的结尾,而这是不可能的;

■因为 if-then-else 是语句,我们必须使用条件运算符去替换,而且,两种情况都必须提供代码([2]和[3])。任何表达式都计算出值,但如果我们省略了 else 分支,并且条件不成立,那么,我们就不会知道返回了什么!

■在 else 分支[3]中的表达式是代码块,包含变量声明,和标准的表达式。声明的局部变量 sumRest,可以在后面的代码块中访问。F# 的变量声明就是用的这种方式。声明不是表达式,而是一种特殊的语法结构,可以被追加到表达式上。

第三章我们会回到值绑定(F# 中相当于变量声明),你将看到在 F# 中是如何运行的。另一个显著不同是,F# 中有专门类型,表示什么也没有。C# 中的 void 关键字,不是真正的类型,因此,不能声明 void 类型的变量;F# 的 unit 类型是真正的类型,它只有一个值,不含有任何信息。F# 中所有的命令式结构都使用这个类型,因此,在调用 Console.WriteLine 方法时,F# 会把它看作是正常的表达式,返回 unit 类型的值。一切都是表达式,使理解代码更容易。在下一节,我们要讨论一个更有意义的内容。

 

2.2.4 用表达式代替语句