5.5.1 Lambda 函数
在 F# 中,lambda 函数创建的函数,与一般使用 let 绑定的声明相同。在 C# 中,没有任何内置函数的概念,因此,使用方法,或者委托,写的 lambda 函数,被转换为委托或表达式树(在补充材料“C# 中从委托到函数”中,有表达式树的详细内容),但是,在 C# 中,不能使用 lambda 函数声明普通方法;委托的使用,像任何其他值一样,因此,可以作为参数值传递给其他方法,相应地,就能用委托在 C# 中写高阶函数了。我们先看一个 F# Interactive 会话,再写类似的 C# 代码。清单 5.15 显示了如何在 F# 中,使用 let 绑定和 lambda 函数语法写函数。
清单 5.15 使用lambda 函数和let 绑定(F# Interactive)
> let square1(a) = a * a;; <-- 使用let 绑定
val square1 : int –> int
> let square2 = fun a -> a * a;; <-- lambda 表示
val square2 : int –> int
> let add = fun a b -> a + b;; [1]
val add : int -> int –> int
> add 2 3
val it : int = 5
我们首先写一个简单的函数 square1,计算给定数的平方,同样的方法,我们之前已经见过多次了。输入之后,F# 打印出签名(值类型),告诉我们它取一个整数,返回一个整数。接下来,我们使用 lambda 表示,声明另一个值 square2,初始化为函数。从输出可以发现,两个声明是等价的。最后,我们声明另一个值[1],这是有两个参数的 lambda 函数的语法。看到这些示例后,也许能够使用lambda 表示法,重写任何有 let 绑定的 F# 函数,反之亦然。
现在,我们看看如何在 C# 中使用 lambda 函数,写出同样的功能:
Func square =
a=> a * a;
Func add =
(a,b) => a + b;
我们使用委托类型 Func,这在 .NET 3.5 中可用。这个委托表示函数,它的类型参数值描述了参数的类型和返回的类型。从技术角度来讲,Func 不是一个委托,而是由根据类型参数数目重载的委托族;每一个表示有不同数量参数的函数。下面是 C# 和 F# 语法之间的主要差异:
■ F# lambda 表达式声明以 fun 关键字开头。
■在 C# 中,在括号内指定多个参数,用逗号分隔;在 F# 中,用空格分隔参数。
■在 C# 中,声明委托值时,必须显式指定类型。
C# 中从委托到函数
正如前面提到过的,在 C# 中的函数使用委托来表示,特别是新的 Func 委托族。在某种意义上,lambda 函数与此委托是革命性的变化,为 C# 增加了函数编程,但它也可以也被看作是C# 中已有功能的自然进化。本书通常取前一种看法,但接下来我们要看一下进化的表现。
在 C# 的第一版中,就已经有了委托,但没有泛型,因此,对于返回和参数类型的每个组合,必须单独声明委托。当创建委托时,代码还必须写在命名方法的内部,因此,写出的代码可能像这样:
delegate int FuncIntInt(int a, int b);
FuncIntInt add = new FuncIntInt(Add);
代码假定有一个 Add 方法,它有两个整数参数,返回一个整数。C# 2.0 向前迈出了一大步,增加了泛型,因此,用 Func(尽管它并不包括在基本类库中)声明泛型委托,用新的匿名方法功能,创建方法,而不要写命名方法了:
delegate R Func(T1 arg1, T2 arg2);
Func add = delegate(int a, int b) { returna + b; }
最后,.NET 3.5 和 C# 3.0 又带来了几个改变,Func 委托已经加到了系统库中,C# 添加了 lambda 表达式,因此,能够以更简洁的方式写出同样功能的代码:
Func<int, int, int> add = (a, b)=> a + b;
Lambda 表达式的另一个重要功能:当声明为Expression (表达式)类型时,能够转换为表达式树(expression trees),这样,就可以把 lambda 表达式的代码看作是数据,并获得 lambda 表达式的源代码表示。这对于使用 LINQ 处理数据库非常重要,但现在,对我们来说还不是关键功能。此外,由于这一功能,我们在声明 lambda 表达式时,不能使用 var 关键字,因为编译器不能确定把它编译成代理(Func),还是保存为表达式树(Expression)。
C# 中的 Func 委托和 lambda 表达式, 与 F# 中的函数相类似,但是,F#从一开始就有函数,所以,它几乎不需要为委托。它支持使用委托,主要是用于互操作性的原因,但是,可能根本用不到。
我们已经看到了 F# 和 C# 中 lambda 函数的几个示例,但现在,还有几个重要的内容需要讨论一下。