首页 | 互联网 | IT动态 | IT培训 | Cisco | Windows | Linux | Java | .Net | Oracle | 软件测试 | C/C++ | 嵌入式开发 | 存储世界 | 服务器
网络设备 | IDC | 安全 | 求职招聘 | 数字网校 | 网页设计 | 平面设计 | 技术专题 | 电子书下载 | 教学视频 | 源码下载 | 搜索 | 博客 | 论坛
中国IT实验室Dotnet频道
中国IT教育
Google
首页 ASP.NET  C#  XML/WebService ADO.NET VC.NET VB.NET .NET 资讯动态 专题 RSS订阅 讨论 下载
您现在的位置: 中国IT实验室 >> Dotnet >> 正文

用Lambda表达式进行函数式编程

  2.声明同时调用

  拜匿名委托所赐,VB的Lambda表达式在任何时候都具有可调用的语义,即使是刚刚声明出来:

Dim result = (Function(a, b) a + b)(1, 2)
'result结果为3

  这种即时调用的用法实现某些逻辑的时候提供了极大的方便。而C#的Lambda表达是不具备即时调用的能力,必须用一个已知委托类型的变量接受它,然后才能通过委托变量调用。

  3.同签名委托间的类型转换

  这是一个VB编译器帮忙实现的小特性,即如果两个委托类型的参数之间有互相兼容的特性(完全一致或可以进行转换),那么两个委托变量之间就可以直接进行类型转换。而且这个转换支持VB的隐式转换规则。后面我们会看到,这个用法对编写动态语义的函数非常有帮助。

Delegate Function A(x As Integer)As Integer
Dim x As New A(Function(i) i + 1)
Dim y As Func(Of Integer, Integer)
y = x '类型转换达成!

  C#并不支持这种类型转换,但C#实现相同的功能也极为容易,你知道怎样写吗?不妨考虑一下。

  以上三点特性加上VB的动态语言特性(隐式类型转换,后期绑定),使得VB的Lambda语法非常接近正牌函数式语言,比如Lisp.当然与现代函数式语言相比,这些功能还显得有些匮乏。下面我们就开始一个有趣的例子:用函数表示数据。Lambda函数的语法能够从表达式所在的上下问中捕获变量,这种称之为“闭包(Closure)”的特性使得函数具有表示数据的能力。例如我们不用Structure或Class之类的语句,就能完全生成一种包含两个成员的对偶结构:

Dim MakePair = Function(u, v) Function(m) If(m = 0, u, v)
Dim pair = MakePair("Asdf", "231")
Console.WriteLine(pair(0)) '第一个字段 
Console.WriteLine(pair(1)) '第二个字段

  您可以看到,MakePair不仅仅自己是一个函数,它调用之后还会返回另一个函数,而后者则捕获了MakePair传入的参数,形成了一个保存数据的结构体。这种返回函数的函数称作高阶函数。高阶函数就是函数式语言与普通命令式语言中函数的最大不同,它将函数的抽象能力提高了一个档次。

  下面我们来看一个更难,更神奇的例子:怎么用Lambda表达式实现递归。众所周知,Lambda表达式是匿名函数,因此他们不能通过引用自己名字的方式进行递归。有人可能想到,将承载Lambda表达式的委托变量捕获到Lambda的闭包中实现递归,但这样违背函数式编程的精神。到底能不能实现纯粹Lambda的递归呢?答案是肯定的,但是需要一个特殊的东西——不动点组合子。

  不动点用一句通俗的话表示就是将它作用于任何函数f后,等同于将f再次调用到整个体系本身上的效果。假设用Y表示不动点组合子,那么Y(f)的效果就等同于f(Y(f))的效果。也就是说我们把调用的体系Y(f)又完整地作为参数传递给了f函数。聪明的你就会发现,这个Y组合子可以用来实现函数的递归。只要让f的内部逻辑将自己的参数视为下一次调用的自身函数即可实现。如此神奇的Y在VB中到底是什么样子呢?如果用无类型的写法,它类似于这样:

Dim Y = Function(f) _
(
Function(h) Function(x) f(h(h))(x)) _
(
Function(h) Function(x) f(h(h))(x))

  这行语句体现了函数式编程精妙之所在,函数Y是一个函数,它接受了一个函数f;接下来Y在内部生成了一个函数,它又以函数h为参数,返回了接受x的第三个函数。在完成了f(h(h))(x)这一连串调用后,Y又将自己内部的函数传递给了自己,并将最终的结果返回。尽管有如此解释,理解不动点组合子Y的真实运算过程是极其费脑子的,如果不能理解,我们可以直接记住Y(f)等同于f(Y(f))这一恒等式。虽然我们写出了Y的定义,但遗憾的是这样并不能执行。因为编译器不能猜出f,h等参数的类型,因此不允许用函数调用的语法访问他们。为了解决这个问题,我们采用VB的后期绑定功能:

Dim Y1 = Function(f) _
(Function(h) Function(x) f.Invoke(h.Invoke(h)).Invoke(x)) _
(Function(h) Function(x) f.Invoke(h.Invoke(h)).Invoke(x)) 

上一页  [1] [2] [3] 下一页

【责编:John】

中国IT教育

相关产品和培训
文章评论
 友情推荐链接
 认证培训
 社区讨论
 博客论点
 Dotnet频道相关导航