3.4.1.2 在 F# 中传递函数作为参数值
在 F# 中的函数 aggregateList 非常类似于我们已经实现的方法,但有一点重要的区别,F# 天然支持把函数作为参数值传递给其他函数,因此,不需要使用委托。
在 F# 中,函数是特殊类型。类似于元组,函数的类型是由其他的基本类型构成。元组类型的代码表示,在元素的类型之间使用星号(例如,int * string);而函数的类型表示,参数的类型和返回的类型。它提供了与 C# 中的委托所实现的同样的类型安全。一个函数,如果取一个数,加上 1,它的类型为 int -> int,表示取一个整数,返回一个整数;如果函数取两个整数,返回一个整数,其类型为 int -> int-> int,这就是 aggregateList 函数第一个参数的类型。清单3.18 是示例的 F# 版本。
清单 3.18 计算列表中元素的和、积(F# Interactive)
> let rec aggregateList (op:int ->int -> int) init list = <- 函数作为参数
match list with
|[] -> init [1]
|hd::tail ->
letresultRest = aggregateList op init tail | [2]
opresultRest head |
;;
val aggregateList : (int -> int ->int) -> int -> int list -> int
> let add a b = a + b
letmul a b = a * b
;;
val add : int -> int -> int | 显示的类型与 op 参数的类型一致
val mul : int -> int -> int |
> aggregateList add 0 [ 1 .. 5 ];; | 立即类型函数
val it : int = 15 |
> aggregateList mul 1 [ 1 .. 5 ];; |
val it : int = 120 |
如同 C# 版本一样,函数的前两个参数说明列表中的元素如何聚合,第二个参数是初始值,第一参数是 F# 函数。在这个示例中,为了使代码更简单,我们只让函数使用整数,因此,第一个参数加上了类型批注(type annotation),说明 op 函数的类型是有两个整数参数,返回一个整数的函数。
接下来,我们看到熟悉的列表处理模式:一个分支用于空列表[1],一个用于 cons cell [2]。在 F# Interactive 中输入 AggregateList 函数的代码后,输出了函数的签名(signature of the function) [3]。函数的签名,第一次看到有点不知所措,但很快就会熟悉了。图 3.2 是签名每部分的意思。
图 3.2 aggregateList 函数的类型签名的详细信息。第一个参数值指定如何聚合两个数字,第二个是初始值,第三个是输入的列表。
最后,我们写了两个简单的函数(add 和 mul)来验证,它们的签名都与 aggregateList 函数的第一个参数的类型相对应。我们写的这两个函数,只是想使示例与前面的 C# 版本保持一致,实际上,F# 能够取任意二元运算符作为参数,把它当作普通的函数一样使用。因此,我们不一定要写加法函数,而直接用加号:
> aggregateList (+) 0 [ 1 .. 5 ];;
val it : int = 15
直接使用运算符,这个功能通常很有帮助,使 F# 代码非常简洁。注意,当使用运算符代替函数时,必须把它括在括号中,不能只写 +,必须要写成 (+)。
我们可能会想,aggregateList 函数不是特别有用,除了加(乘)列表中的元素以外,没有其它用途,但下一节,会有更惊奇的例子。