C++函数式编程(二)纯函数

来源:互联网 发布:微米wm 未授权域名 编辑:程序博客网 时间:2024/05/21 09:23

原文:http://www.altdevblogaday.com/2012/04/26/functional-programming-in-c/

作者:John Carmack


-潘宏 译

-2013年1月

-email: popyy@netease.com

-weibo.com/panhong101



纯函数(Pure Function)与外界交换数据只有唯一渠道——参数和返回值。其在逻辑上没有副作用(译注:副作用就是潜在地和函数的外部环境交换数据)——这当然只是从抽象角度来说。从现实角度讲,任何函数在CPU级别上都存在副作用,大多数函数在堆级别上也有副作用。可就算有这些副作用,这种在逻辑层面上的抽象也是有意义的。


纯函数不读写全局变量,无状态、无IO,不改变传入的任何参数。理想情况下,不会给他传入任何外部数据,因为一旦传入一个诸如allMyGlobals的指针,上面的这些规则就都被打破了。


纯函数有诸多很棒的特性。


线程安全性。只有传值参数的纯函数是完全线程安全的。如果有引用或者指针类型的参数,就算是const类型的,你也得注意:另一个会执行non-pure操作的线程可能会改变或者释放这些引用或者指针,这很危险。可是,即使在这种情况下存在隐患,纯函数仍不失为一种安全编写多线程程序的有力工具。


你可以轻易地将纯函数应用于并行计算领域,或者多次计算然后比较结果。这种方式更易于对系统进行试验和扩展。


重用性。很容易把一个纯函数移植到一个新的运行环境。你可能仍然需要修改类型定义以及这些被调用纯函数,但这不会引起滚雪球效应。而这种效应往往表现为:很多时候,你需要在另外一个系统中使用你之前的一些代码,因此你得把那些代码从原来的系统中提取出来,然后把它们融合进新的系统,可这个时候你发现,提取这些代码比重写一个还费事。


可测试性。纯函数具有引用透明性(Referential Transparency)。也就是说,对于同一个输入值,它一定产生相同的输出值,而与什么时候执行该函数无关。而其它一些和系统相互耦合的机制,与纯函数相比,在测试的可操作性方面都逊色不少。此外,我本人从来都没有非常负责任地编写过测试代码——那种在足够多的系统上都跑过的,经过精心安排的测试。而且我竟然还会经常安慰自己(我错了):这种测试其实是没必要的。但是,纯函数却很容易测试,就像教课书中所描述的:安排一些输入,然后观察输出。现在,每当我遇到一些看起来比较复杂的代码,我都会把它们剥离出来并放入一个单独的纯函数中,写一些用例来测试它们。而令我感到惊恐的是,代码经常会测出问题,我还真是想的不够周全。


可理解性和可维护性。纯函数在输入输出值上的限定性,让长时间没有接触并对它们感到陌生的程序员,在需要的时候很容易回过头来,重新理解它们。此外,一些涉及程序外部状态的,没有文档记录在案的需求,也已经被纯函数本身的特性所杜绝。


形式系统(Formal Systems)和自动推理软件(Automated Reasoning About Software)以后会越来越重要。而静态代码分析(Static Code Analysis)目前已经很重要了,它会把程序转化进更加函数式化的辅助分析工具,或者,至少能够用快速的局部测试工具代替低速的、昂贵的全局测试工具,并保持相同的测试覆盖面。我们正处于一个“任务导向型”产业之中,以“程序全部都正确”为目标我们还不敢奢望,能够证明我们的代码库中的关键部分都没问题,在目前来看还是比较有现实意义的。在开发过程中,我们可以使用更科学严谨的方法。


某人面对着一个新手级别的程序示例,可能会一边挠头一边嘀咕:“是不是所有的程序都是这么写的?”。而现实情况往往就是这样:存在着大量的“大泥球“(Big Balls of Mud,译注:指没有清晰结构的系统)。传统编程语言给了开发者各种各样的语法甜头,而且大家的态度就是能用就用。如果你觉得你写的代码是“一次性”的,那么怎么方便就怎么来吧,你可能会大量使用全局变量。但如果你希望你的代码一年后还能用,那就权衡一下,你现在的方便可能会变成你以后的痛苦。大多数开发者都不太擅长预见自己将来因代码修改而带来的代码集成痛苦。

原创粉丝点击