5.5.2.1 函数作为参数值和返回值
在第三章,我们已使用过,在 C# 和 F# 中把函数作为参数值,因此,这些基本概念不应该是新的;然而,我们还没有以这种方式使用过 lambda 函数。Lambda 函数是把函数写成另外函数的参数值的最简单方法。清单 5.17 提供了一个简单的示例。在清单中前面的函数,参数为一个数和一个函数,两次调用这个函数,第一次调用的结果作为第二次调用的参数值。
清单 5.17 在 C# 中 F# 中,函数作为参数值
// C# version
int Twice(int input, Func f) { [1]
return f(f(input));
}
var result = Twice(2, n => n * n); [2]
// F# version
> let twice (input:int) f =f(f(input));; [3]
val twice : int -> (int -> int) –>int [4]
> twice 2 (fun n -> n * n);; [5]
val it : int = 16
在这个示例中,我们在一个地方看到所有重要的功能。它显示如何声明 C# 方法和 F# 函数,把函数作为参数值([1]和[3]),以及如何使用 lambda 函数调用它们([2]和[5])。在 F# 中,我们使用到类型批注,告诉 F# 编译器,我们只需要整数。我们在下一章可以看到,没有这个批注,可能自动使函数更为通用。这通常是可取的,但我们希望保持示例尽可能简单。
在 C# 版本中,Twice 是方法,把委托作为参数,而在 F# 版本中,它是函数。当我们看到 F# 签名时[4],能够发现,它的构造就是函数类型构造器(箭头);第二个参数是函数,参数为一个整数,返回一个整数,整体类型是有两个参数的函数。
因为函数就是普通值,因此,我们写的函数(或 C# 中的方法),也可以把函数作为结果返回。清单 5.18 是一个函数,参数为一个数字,返回一个函数,把这个数字和任何给定的参数值加起来。
清单 5.18 在C# 和 F# 中,把函数作为返回值
C# | F# |
Func Adder(int n) { return (a) => a + n; [1] }
Func add10 = [3] Adder(10);
var r = add10(15); [5] // Result: r == 25 | > let adder(n) = (fun a -> a + n);; [1] val adder : int -> int –> int [2]
> let addTen = adder 10;; [3] val addTen : (int -> int) [4]
> addTen 15;; [5] val it : int = 25 |
Adder 的参数为一个整数[1],返回结果为一个函数。在C# 中,显式指定了返回类型,是 Func 委托,而在 F# 中,返回类型是由类型推断推导出来的,是函数,其类型为 int -> int。
后面我们会看到,输出的类型签名表明,函数的参数为整数,返回函数[2]。如果我们给输出的签名加上括号,可以看得更清楚,那么,会写成 int-> (int-> int)。
调用返回函数的函数(或 C# 的方法)[3],在 C# 中,结果是委托,在 F# 中,是普通的函数。正如输出的类型签名所示的[4],参数为整数,返回整数。
调用返回的函数(或 C# 中的 Func 委托),在 F# 中,我们把它作为普通的函数使用,在 C# 中,把它作为委托调用。
清单 5.18 使用一个简单的示例,展示了函数作为返回值,但在后面几章中我们将会看到,从一个函数返回另一个函数,更有用。关于这个代码,有一件事值得进一步解释。如果我们看看 F# Adder 函数的类型签名,可以发现,其类型是 int-> int-> int,。这看起来像有两个参数的函数,而如果像 int-> (int-> int),可能更容易理解。它们的意思是一样的,因为,F# 和其他函数语言,对有多个参数的函数,通常表示方法不同,[当然是]按通常的面向对象所理解的。