分享

编程语言的发展趋势及未来方向(3)

 guitarhua 2012-04-15

编程语言的发展趋势及未来方向(3):函数式编程

2010-05-24 17:56:41 阅读(127) 发表评论

  那么使用函数式编程的形式写代码,你的Point类还是可以包含状态,例如x和y,不过它们是readonly的,一旦初 始化以后就不能改变了。 MoveBy方法不能改变Point对象,它只能创建一个新的Point对象并返回出来。这就是一个创建新Point对象的函数,不是吗?这样就可以让调 用者来决定是使用新的还是旧的Point对象,但这里不会有产生副作用的情况出现。

  在函数式编程里自然不会只有Point对象,例 如我们会有集合,如Dictionary,Map,List等等,它们都是不可变的。在函数式编程中,当我们向一个List里添加元素时,我们会得到一个 新的List,它包含了新增的元素,但之前的List依然存在。所以这些数据结构的实现方式是有根本性区别的,它们的内部结构会设法让这类操作变的尽可能 高效。

  在函数式编程中访问状态是十分安全的,因为状态不会改变,我可以把一个Point或List对象交给任意多的地方去访问,完 全不用担心副作用。函数式编程的十分容易并行,因为我在运行时不会修改状态,因此无论多少线程在运行时都可以观察到正确的状态。两个函数完全无关,因此它 们是并行还是顺序地执行便没有什么区别了。我们还可以有延迟计算,可以进行Memorization,这些都是函数式编程中十分有趣的方面。

   你可能会说,那么我们为什么不都用这种方法来写程序呢?嗯,最终,就像我之前说的那样,我们不能只让CPU发热,我们必须要把计算结果表现出来。那么我 们在屏幕上打印内容时,或者把数据写入文件或是Socket时,其实就产生了副作用。因此真实世界中的函数式编程,往往都是把纯粹的部分进行隔离,或是进 行更细致的控制。事实上也不会有真正纯粹的函数式编程语言,它们都会带来一定的副作用或是命令式编程的能力。但是,它们默认是函数式的,例如在函数式编程 语言中,所有东西默认都是不可变的,你必须做些额外的事情才能使用可变状态或是产生危险的副作用。此时你的编程观念便会有所不同了。

   我们在自己的环境中开发出了这样一个函数式编程语言,F#,已经包含在VS 2010中了。F#诞生于微软剑桥研究院,由Don Syme提出,他在F#上已经工作了5到10年了。F#使用了另一个函数式编程语言OCaml的常见核心部分,因此它是一个强类型语言,并支持一些如模式 匹配,类型推断等现代函数式编程语言的特性。在此之上,F#又增加了异步工作流,度量单位等较为前沿的语言功能。

  而F#最为重要的 一点可能是,在我看来,它是第一个和工业级的框架和工具集,如.NET和Visual Studio,有深入集成的函数式编程语言。F#允许你使用整个.NET框架,它和C#也有类似的执行期特征,例如强类型,而且都会生成高效的代码等等。 我想,现在应该是展示一些F#代码的时候了。

   首先我想先从F#中我最喜欢的特性讲起,这是个F#命令行……(打开命令行窗口以及一个F#源文件)……F#包含了一个交互式的命令行,这允许你直接输 入代码并执行。例如输入5……x等于5……然后x……显示出x的值是5。然后让sqr x等于x乘以x,于是我这里定义了一个简单的函数,名为sqr。于是我们就可以计算sqr 5等于25,sqr 10等于100。

   F#的使用方式十分动态,但事实上它是一个强类型的编程语言。我们再来看看这里。这里我定义了一个计算平方和的函数sumSquares,它会遍历每个列 表中每个元素,平方后再把它们相加。让我先用命令式的方式编写这个函数,再使用函数式的方式,这样你可以看出其中的区别。

let sumSquaresI l = 
  let mutable acc = 0 
  for x in l do 
    acc <- acc + sqr x 
  acc

  这里先是命令式的代码,我们先创建一个累加器acc为0,然后遍历列表l,把平方加到acc中,然后最后我返回acc。有几件事情值得注意,首先为了创建一个可变的状态,我必须显式地使用mutable进行声明,在默认情况下这是不可变的。

   还有一点,这段代码里我没有提供任何的类型信息。当我把鼠标停留在方法上时,就会显示sumSquaresI方法接受一个int序列作为参数并返回一个 int。你可能会想int是哪里来的,嗯,它是由类型推断而来的。编译器从这里的0发现acc必须是一个int,于是它发现这里的加号表示两个int 的相加,于是sqr函数返回的是个int,再接下来blablabla……最终它发现这里到处都是int。

  如果我把这里修改为浮点数0.0,鼠标再停留一下,你就会发现这个函数接受和返回的类型都变成float了。所以这里的类型推断功能十分强大,也十分方便。

  现在我可以选择这个函数,让它在命令行里执行,然后调用sumSquaresI,提供1到100的序列,就能得到结果了。

let rec sumSquaresF l = 
  match l with 
  | [] -> 0 
  | h :: t -> sqr h + sumSquaresF t

   那么现在我们来换一种函数式的风格。这里是另一种写法,可以说是纯函数式的实现方式。如果你去理解这段代码,你会发现有不少数学的感觉。这里我定义了 sumSqauresF函数,输入一个l列表,然后使用下面的模式去匹配l。如果它为空,则结果为0,否则把列表匹配为头部和尾部,然后便将头部的平方和 尾部的平方和相加。

  你会发现,在计算时我不会去改变任何一个变量的值,我只是创建新的值。我这里会使用递归,就像在数学里我们经常使用递归,把一个公式分解成几个变化的形式,以此进行递归的定义。在编程时我们也使用递归的做法,然后编译器会设法帮我们转化成尾递归或是循环等等。

  于是我们便可以执行sumSquaresF函数,也可以得到相同的结果。当然实际上可能你并不会像之前这样写代码,你可能会使用高阶函数:

let sumSquares l = Seq.sum (Seq.map (fun x -> x * x) l )

  例如这里,我只是把函数x乘以x映射到列表上,然后相加。这样也可以得到相同的结果,而且这可能是更典型的做法。我这里只是想说明,这个语言在编程时可能会给你带来完全不同的感受,虽然它的执行期特征和C#比较接近。

  这便是关于F#的内容。

  文章来源:http://blog./2010/05/trends-and-future-directions-in-programming-languages-by-anders-3-functional-programming-and-fsharp.html

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多