关闭

用C++进行函数式编程

发表于:2012-7-26 09:16

字体: | 上一篇 | 下一篇 | 我要投稿

 作者:王江平 译    来源:51Testing软件测试网采编

  或许本文的每位读者都听说过,当初“函数式编程”(Functional Programming)肩负着为软件开发带来福祉的期望来到这个世界,大家可能还听说过有人将它奉为软件开发的银弹。然而,上维基百科查看更多信息却让人大倒胃口,一上来就引用λ演算和形式系统。很难一眼看出这跟编写更好的软件有什么关系。

  我的实效性总结:软件开发中的大部分问题都缘于程序员没有完全理解程序执行中所有可能的状态。在多线程环境中,这一理解的缺失以及它所导致的问题变得更加严重,如果你留意这些问题,会发现它几乎严重到令人恐慌的地步。通过函数式的风格编写程序,可以将状态清晰地呈现给你的代码,从而使代码的逻辑更易于推理,而在纯粹的函数式系统中,这更使得线程竞争条件成为不可能的事情。

  我确实相信追求函数式的程序设计有着实实在在的价值,然而劝说所有程序员抛弃C++编译器,转而启用Lisp、Haskell,或者干脆说任何其他边缘语言,那是不负责任的。让语言设计者永远懊恼的是,总会有大量的外在因素压跨一门语言的好处,相对大多数领域来说,游戏开发尤其如此。除了大家都要面对的遗留代码库和有限的人力资源问题之外,我们还有跨平台问题、私有工具链、证书网关、需要授权的技术,以及严酷的性能要求。

  如果你的工作环境中可以用非主流语言完成主要开发任务,那应该为你欢呼,不过也等着打板子吧,罪名是项目进展方面的。而对所有其他人:不论你用何种语言工作,通过函数式的风格编写程序都会带来好处。任何时候,只要方便,就应当这么做;而不方便时,也应当仔细想想自己的决定。以后,只要愿意,你可以学学lambda、monad、currying、在无限集上合成懒惰式求值的函数,以及显式面向函数式语言的所有其他方面。

  C++语言并不鼓励函数式程序设计,但它也不妨碍你这么做,而且为你保留了深入下层、运用SIMD内在函数基于内存映射文件直接布局数据的能力,或任何其他你发现自己用得着的精华特性。

  纯函数

  纯函数是这样一种函数:它只会查看传进来的参数,它的全部行为就是返回基于参数计算出的一个或多个值。它没有逻辑副作用。这当然只是一种抽象;在CPU层面,每个函数都是有副作用的,多数函数在堆的层面上就有副作用,但这一抽象仍然有价值。

  纯函数不查看也不更新全局状态,不维护内部状态,不执行任何I/O操作,也不更改任何输入参数。最好不要传递任何无关的数据给它——如果传一个allMyGlobals指针进来,这一目标就基本破灭了。

  纯函数有许多良好的属性。

  ● 线程安全:使用值参数的纯函数是彻底线程安全的。使用引用或指针参数的话,就算是const的,你也应当知晓一个执行非纯操作的线程可能更改或释放其数据的风险。但即便是这种情况,纯函数仍不失为编写安全多线程代码的利器。你可以轻松地将一个纯函数替换为并行实现,或者运行多种实现并比较结果。这让代码的试验和演化都更加便利。

  ● 可复用性:移植一个纯函数到新的环境要容易很多。类型定义和所有被调用的其他纯函数仍然需要处理,但不会有滚雪球效应。有多少次,你明明知道另一个系统有代码可以实现你的需要,但要把它从所有对系统环境的假设中解脱出来,还不如重写一遍来得容易?

  ● 可测试性:纯函数具有引用透明性(referential transparency),也就是说,不论何时调用它,对于同一组参数它永远给出同样的结果,这使它跟那些与其他系统相互交织的东西比起来更易于使用。在编写测试代码的问题上,我从来没有特别尽责;太多代码与大量系统交互,以至于使用它们需要相当精细的控制,而我常常能够说服自己(也许不正确)这样的付出并不值得。纯函数很容易测试,其测试代码就像直接从教料书上摘抄下来的一样:构造一些输入并查看结果。每次遇到一小段目前看起来有些奇技淫巧的代码,我都会把它拆成一个单独的纯函数并编写测试。可怕的是,我常常发现这样的代码中存在问题,意味着我撒下的测试安全网还不够大。

  ● 可理解性与可维护性:输入和输出的限制使得纯函数在需要时更易于重新学习,由于文档不足而隐藏了外部信息的情况也会更少。

  形式系统和软件的自动推理将来会越来越重要。静态代码分析今天已经很重要了,将代码转换成更加函数式的风格有助于工具对它的分析,或者至少能让速度更快的局部工具所覆盖的问题跟速度慢且更加昂贵的全局工具一样多。我们这个行业讲的是“把事情做出来”,我还看不到关于整个程序“正确性”的形式证明能成为切实的目标,但能够证明代码的特定部分不存在特定种类的问题也是很有价值的。我们可以在开发过程中多运用一些科学和数学成果。

  正在修编程导论课的同学可能一边挠头一边想:“不是所有的程序都要这么写吗?”现实情况却是“大泥球”(Big Balls of Mud)程序多,架构清晰的程序少。传统的命令式编程语言为你提供了安全舱口,结果它们就总是被使用。如果你只是写一些用一下就扔掉的代码,那就怎么方便怎么来,用到全局状态也是常事。如果你在编写一年之后仍将使用的代码,那就要将眼前的便利因素跟日后不可避免的麻烦平衡一下了。大部分程序员都不擅长预测日后改动代码将会导致的各种痛苦。

31/3123>
《2023软件测试行业现状调查报告》独家发布~

关注51Testing

联系我们

快捷面板 站点地图 联系我们 广告服务 关于我们 站长统计 发展历程

法律顾问:上海兰迪律师事务所 项棋律师
版权所有 上海博为峰软件技术股份有限公司 Copyright©51testing.com 2003-2024
投诉及意见反馈:webmaster@51testing.com; 业务联系:service@51testing.com 021-64471599-8017

沪ICP备05003035号

沪公网安备 31010102002173号