函数式编程学习之路(七)

函数式程序设计为什么至关重要?

模块化

软件越来越庞大,越来越复杂,要解决复杂问题,必然会产生大量代码。系统很容易就膨胀到人力无法控制的局面。

我们看看这些范型怎么解决这个问题:

结构化模式:模块化,层次分解。把大块分成小块,再分成小小块,直到人力能轻松处理为止。

面向对象:组件化,把东西设计成小个小个的独立个体,再通过组装成型更大的个体,直到它能工作为止。

函数式:粘合,每一个函数就象一颗螺丝一个零件,通过粘合形成一个更大更复杂的功能。

由此可见,函数式更象我们人类发明或制造系统,把铁块和木棒粘合,就会成形成一把锤子。

再对比下这三种常见范型的差异,结构化层次太多时,会产生结构上的复杂性。上层的变化,会导致下层大量细粒度的变化。在现化软件系统中,我们更常见把它作为上层设计。比如分层,分模块。分功能。

面向对象。当组合或继承层次过多时,对象会变得相当复杂。太多的方法和属性,比如常见的框架类库。往往只要1个功能却要引入10个功能模块。

函数式:函数式的特点就是,粘合把复杂性包在了内部。对外的接口是不会变的(正常情况下就是一个输入一个输出),象是大众的套娃车,不管怎么套,就是一个壳。

粘合比组合还有个更大的差别,组合是1+1=2.一个特性组合一个特性,就是两个特性。

而函数式的粘合,是一个特性粘合另一个特性,会变成其它非常的特性。会产第前两个特性都没有的功能,比如铁块粘合木棒。会变成锤子,再粘合镰刀,会变成Dang的标志。一种你完会想象不到的结果。

那么怎么粘呢?

把函数粘起来
    两种黏合剂中的第一种,使简单的函数可以聚合起来形成复杂的函数。
    通过将一个简单的函数(sum)模块化为一个“高阶函数”与一些简单参数的聚合,我们得到了一个部件(reduce),它可以用于编写与列表有关的许多函数,而又不再需要(更多的)编程努力。不止是对有关列表的函数可以这么干,对于树结构,我们不再给出一个函数例子并将它抽象为高阶函数,取而代之的是,直接给出一个类似于reduce的函数redtree。以上这些操作之所以可行,是因为函数式语言允许将传统型语言中不可分解的函数表达为一些部件的聚合,也就是一个泛化的高阶函数与一些特化函数的聚合。这样的高阶函数一旦定义,便使得很多操作都可以很容易地编写出来。不论何时,只要一个新的数据类型被定义,就应当同时定义用于处理这种数据的高阶函数。这样就简化了对数据类型的处理,同时也将与它的表示细节相关的知识局部化了。与函数式语言最相像的传统程序语言是可扩展语言,只要有需求,这种程序语言就好像随时都可以扩展出新的控制结构一样。

把程序粘起来

函数式语言提供的另一种黏合剂使得所有程序都可以粘在一起。回忆一下,一个完整的函数式程序只不过是一个从输入映射到输出的函数。如果f和g是这样的程序,那么对程序(g.f)当提供了输入参数input之后,得到: g (f input)

程序f计算自身的输出,此输出被用作程序g的输入。传统上,这是通过将f的输出储存在临时文件中实现的。这种方法的毛病是,临时文件可能会占用太大的空间,以至于将程序黏合起来变得很不现实。函数式语言提供了一种解决方案。程序f和g严格地同步运行,只有当g试图读取输入时,f才启动,并且只运行足够的时间,恰好可以提供g需要读取的输出数据。而后f将被挂起,g将继续执行,直到它试图读取另一个输入。一个额外的好处是,如果g没有读取完f的全部输出就终止了,那么f也将被终止。f甚至可以是一个不会自行终止的程序,它可以产生无穷多的输出,因为当g运行结束时,f也将被强行终止。这就使得终止条件可以与循环体分离——一种强大的模块化形式。

这种求值方式使得f尽可能地少运行,因此被称为“惰性求值(lazy evaluation)”。它使得将程序模块化为一个产生大量可能解的生成器与一个选取恰当解的选择器的方案变得可行。有些其他的系统也允许程序以这种方式运行,但只有函数式语言对每一个函数调用都一律使用惰性求值,使得程序的每个部分都可以用这种方式模块化。惰性求值也许是函数式程序员的拿手利器中威力最大的模块化工具。

如一个函数来计算: repeat f a = cons a (repeat f (f a)),只要可能,可以不停的计算它的无穷极限值。

微分积分(这里函数式科学家的老毛病又犯了,总是喜欢把函数式用去搞数学计算,是不会受到广大程序员欢迎的,惰性求值需要在网络,HTML,数据库这样的地方有实实在在的应用).

人工智能中的例子

我们已经指出,函数式语言威力强大主要是因为它们提供了两种新的黏合剂:高阶函数和惰性求值。在本节中,我们将讨论人工智能中一个大一点的实例,并演示如何使用这两种黏合剂来十分简单地编写它。块化工具。alpha-beta“启发式搜索”,一个用于估计游戏者所处形势好坏的算法。该算法预测游戏局势的可能发展,但会避免对无意义局势的进一步探究。

(函数式科学家的老毛又又犯了。人工智能,不是大多数据程序员喜欢搞的。数学和人工智能只会阻碍大多数程序员对函数式的应用。广大程序员还有很多初级问题需要解决。还谈不上数学计算和人工智能。)

结论

在本文中,我们指出模块化是成功的程序设计的关键。以提高生产力为目标的程序语言,必须良好地支持模块化程序设计。但是,新的作用域规则和分块编译的技巧是不够的——“模块化”不仅仅意味着“模块”。我们分解程序的能力直接取决于将解决方案粘在一起的能力。为了协助模块化程序设计,程序语言必须提供优良的黏合剂。函数式程序语言提供了两种新的黏合剂——高阶函数与惰性求值。利用这些黏合剂可以将程序用新的、令人激动的方式模块化,对此我们举出了很多实例。越小、越通用的模块越可能被广泛地重用,使后续的程序设计工作变得简单。这解释了为什么函数式程序与传统型程序比较,要小得多,也容易编写得多。它也为函数式程序员提供了一个追求目标。如果程序的任何部分是杂乱或者复杂的,那么程序员就应当尝试将其模块化并泛化其部件。他应当期望把高阶函数和惰性求值用作他做此事的工具。

posted @ 2013-04-24 21:29  人工智能-群513704292  阅读(461)  评论(0编辑  收藏  举报