地道的F# - 函数式 vs. 命令式

来源:互联网 发布:app软件破解器 编辑:程序博客网 时间:2024/06/01 10:21

我们的故事要从这个叫Stuart Bowers的家伙开始,下图中,他正拿着一个来自Café Press网站的杯子喝着咖啡,此杯子正是他对函数式编程做出出色贡献的象征。

 

 IMAGE_154

StuartAmalga(一个微软推出的医疗系统)项目的一个开发者,也是微软公司的一位全职员工。此人不写博客——此乃世界的一大损失也

 

不管怎样,他还是帮我审读过我写的书中的几个章节,并且最近我们还有一次促膝长谈,讨论并分享怎样才是一个地道的F#风格的话题。

 

问题

 

提问:在编写下面的代码时,怎样才是“正确的F#方式”:

创建一个共享的,可变得值并且生成聊个线程池用来累加一个ListF#中的链表)中的元素。(故意创建一个竞争的环境,此乃这个代码示例的关键所在。)

 

命令式(?)方法

让我们以如何使用命令式方式来回答此问题作为开始。

命令式的代码非常的直观且提供如下一些好处:

  • 简单。只需要两个调用QueueUserWorkItem的线程和一些布尔标量来跟踪是否线程完成了,而不是整个正在运行的线程。

然而,这也有一些弊端:

  • 冗余。两个传入QueueUserWorkItem的表达式本质上是一样的,因此,此代码是重复的。如果你想调整他们,例如添加日志,那么你或者会忘记在两个地方都更新代码,或者会仅仅添加代码两次。
  • 数据隐藏。这两个线程最大的区别在于他们累加的数组元素的下标范围。第一个数组从0计算到len/2 1,第二个线程从len/2len。然而,关键的因素被隐藏在代码中两个不同的位置。

 

函数式(?)方法

现在让我们来看看如何用更函数式的风格实现它。

让我们先来看看这个函数式风格的弊端:

  • 太多的变量值。在这个函数里有7个变量值绑定——想必它们都是必要的。
  • 概念数。这个更函数式的实现当然要求你要理解一些F#编码原则。(虽然把线程代码匹配到一个QueueUserWorkItem是相当流行的)

虽然可以说是混淆,但函数式风格提供了一些明显的好处:

  • 重要的数据可以被抽出,而不是硬编码"0 to len /      2""len / 2"这些值,计算数组元素的总数的边界被明确地抽出放在连续的代码行上。这使得声明离一误差(OBOE详见维基百科)更容易地被找到和修复。
  • 代码重用,而不是硬编码这两个线程池函数,一个需要明确边界的sumFlements函数被引入来求和。

就我而言,我部分地支持第一个例子的简单,但是第二个例子在调用范围值的价值不应该被忽视。我最喜欢F#的一个地方是它是多范式的语言,即通常情况下有一个以上的方法来解决问题,这意味着你有多个选择。

 

原文链接:http://blogs.msdn.com/b/chrsmith/archive/2009/04/23/idiomatic-f-functional-vs-imperative.aspx