FP与IP的区别讨论

首先,本文讨论的不是LAMBDA算子与图灵机的等价关系。或者至少不是“在”讨论它们之间的关系。毕音,虽然现在的计算机都是图灵机的等价实现,但LAMBDA算子并不等于FP。

一个程序就是一个状态机。

状态机的4个要素:

状态机可归纳为4个要素,即现态、条件、动作、次态。这样的归纳,主要是出于对状态机的内在因果关系的考虑。“现态”和“条件”是因,“动作”和“次态”是果。详解如下:

①现态:是指当前所处的状态。

②条件:又称为“事件”。当一个条件被满足,将会触发一个动作,或者执行一次状态的迁移。

③动作:条件满足后执行的动作。动作执行完毕后,可以迁移到新的状态,也可以仍旧保持原状态。动作不是必需的,当条件满足后,也可以不执行任何动作,直接迁移到新状态。

④次态:条件满足后要迁往的新状态。“次态”是相对于“现态”而言的,“次态”一旦被激活,就转变成新的“现态”了。

实际上,上面的观点是相对静态的。因为现态与条件其实是没有区别的。对于状态机来讲,在任意一个时点,它都具有确定的状态。这是任何因果系统或决定论系统必须具有的特征(其实,即使是混沌论系统。这一点也是成立的)。这个状态,是前面所有发生事情的结果,又是后面所有事情发生的输入。即我们可以用现态,动作,次态来表示系统。站在系统的角度,现态就是输入,次态就是输出。

这里要注意一下的是,所谓输入输出,都是主观词汇。不是客观存在的东西。客观存在的仍然只有状态及状态的转换。抽象的目的在于去掉状态的主体。不考虑主体。纯粹考虑状态本身。但状态显然是事物的状态。一提状态人们必须想到事物本身。于是思考很难继续。所以必须抽象掉它的主体。最好的办法当然是换个词汇(这个其实是借鉴或来自于数学的思路------一个数字其实什么也不是。但我们的确用它处理了很多的问题------如果你算数的时候老想着事物本身。。。。。。计算的确是很难继续的。更别提更进一步的分析------所以说,现在的很多人对抽象数学的态度。其实都是错误的。完全错误的。因为抽象本身永远都不会是有错的------如果有错的话,那就是你把抽象给用错了地方------这很有可能是数学研究中一个很大的盲区------能不能抽象的问题。怎么抽象的问题------你永远都不可能越过哲学去做任何事情------你一定要越过,就很可能犯错误)。

偏题了?

其实没有。

先回一下FP。

FP的思想就是输入,输出。没有变量。也就是说,没有状态。所有的事情都通过输入输出搞定。也就是说,它已经变成了纯数学(也即说,其已经跳出了最基础的语言环境---哲学---裸奔去了!)。。。。。。这样很可能会犯错误。但只是很“可能”,不是一定。

这里先不管数学会不会犯错误,因为毕竟它不是现在的主题。

先看FP。

根据状态机的状态输入输出本质,只要能把FP的输入输出与状态扯上关系,FP就与状态机是等价的。

1,状态机:

输入输出的是固定时点的系统。即,系统的输入是系统的不同时点的状态。是抽象的,并不真实存在的东西。或者说,它只在某个时间存在。一过了这个时间,它就不存在了。不存在了,自然就不可改变了。它已经成了历史,而历史(真正的历史)是不可能改变的。

不可能改变的输入或状态,是状态机的工作基础。

演绎型系统(现在的计算机系统之所以会采用这种模型,一个原因是图灵与冯诺依曼;另一个原因是,现在的系统其实最开始是大计算器发展过来的。INTEL的第一代8位处理器4004就是为一家计算器公司设计的)。

证明论系统。

强调状态的演绎,不是计算。

2,FP:

演绎型系统太繁琐,在上面进行大型或秒显复杂的运算会很吃力。这种吃力表现为函数在两个平台上的同时出现。只不过因为时间长了,使用者逐渐分化为两个派别:一个派别坚持同时使用状态演变与函数运算两种方式进行编程;而另一个派别为了科学计算的方便,则坚定地去掉了状态演变这个特性,使得它变成了一个纯粹的计算型系统(现在大都叫纯粹的“函数型“系统。我认为不妥。因为如果说纯函数型系统,也只是从数学的意义去区分的。目的还是为了突出这种系统的数学特性。既然是数学特性,我觉得“计算”比“函数”要更明确一点------因为这样的语义更清楚。能帮助人更好地理解这种系统,或进一步,能帮助更好地理解两种系统的区别。以支持更进一步的系统选型或其它方面的决策)

计算型系统。预测型系统。用重复的输入输出过程完成状态的演变。强调计算过程,不是状态演变。

计算型系统指那种给定输入,然后进行一系列的计算以得到结果的系统。但是,现在最流行的图灵机系统并不是这么设计的(如前所述,图灵机属于状态机。状态机遵循的是演绎型系统),它是一个状态演绎系统。而状态是会变的。所以,要想在这种系统上模拟(很难理解会有人想在演绎型系统上建立一个计算型系统,然后再用它来提供演绎型服务------不可思议。但现在的函数型语言起源于计算需求是事实。只不过,当一种语言为人所知后是很难再去控制别人对它的用途的------这一点完全相似于会有人在演绎型系统上进行计算型活动的事实------社会并不关心它自己行为的一致性。这是社会与人最大的区别。因为它没有一致的意志)状态(演绎)型服务,就必须解决可变输入的问题。

因为对状态机来说,输入的是系统在某一个时点的状态。这个状态虽然不一定是唯一的,但它一定是不可变的(一个时“点”,怎么变化?记住,变化是相对时“间”而言的。没有时“间”,就没有变化)。这个又是由时间的单向流动性决定的。因为时间是单向流动的,所以时间不会倒退,历史是不会重演的!

但是因为对真正的“计算机”来说,它是一个“计算”型系统。它通过“计算”来完成状态的输入输出。也就是说,它的输出,是“算”出来的。它的工作模型是,根据输入“计算”输出。这跟演绎型系统的“演绎”模型截然不同。即,虽然两者都工作在状态虚拟的基础上,但是它们得到结果的途径却完全不同。演绎型系统尝试通过演绎得到想要的结果。是一种证明论手段;而计算型系统则是通过抽象的计算得到结果。是一种不严谨的手段(都说数学是最严谨的学科,但这并不代表“计算”也是严谨的。因为数学的严谨只是纯粹存在于其内部的严谨,与对它的应用过程(“计算”。存在于其外部)的严谨毫无关系。真正的“计算”过程其实是一个纯抽象的过程。它丝毫不受因果论或决定论的约束,当然也不属于任何形式的因果系统。比如对任何使用到数学的学科来讲,其计算方法本身的正确性数学是不管的------否则就不会需要这么多XXX学家了。直接全部都去学数学就可以了!------记住,数学是工具。工具是无所谓对错的。至于结论的严谨性,它就更与其没有任何关系了)。但即使这样,两者基于计算的基础仍然是一样的:即都为输入状态。区别只是纯粹的“计算”型系统要求“输出”动作必须发生在“计算”动作之后(其实这一点对于演绎型系统来说同样成立。只不过如前面提过的,演绎型系统的侧重点在于相对其本身更宏观的系统“演绎”,而不是更微观的“计算”,所以表现不太明显罢了------它把这个任务交给了单线程或并发锁等并发控制机制),而演绎型系统因为强调的是“演绎”而不是“计算”,所以导致不管是其本身的表现还是人们对它的察觉,都没有太明显的“计算”痕迹。因此也没有太明显的与输出和计算两者的动作次序相关的要求(只是不是太明显。但有时候,比如涉及到并发执行时的并发控制,和没有涉及到并发时候的处理器的乱序逻辑控制---如酷睿,或无乱序机制的处理器中对指令的顺序执行机制---如凌动,也会有这样的要求。。。。。。总之,只要与计算相关的,必须具有一定的输入锁定机制。只不过因为“计算”型系统本身着重解决的就是计算的问题,在输入锁定这一点上表现得比较演绎型系统更突出罢了)。

这些就是函数型语言要求变量不可变的背景。 

即,FP的工作模型是先锁定输入,然后进行计算,最后得到结果(输出)。其中的输入输出与状态机的输入输出是一致的,都可以用来表示状态。只不过状态机的输入输出本来就是状态,而FP的输入输出需要一个主观的“认为”过程。但只要我们这么干(即“认为”)了,那么它的输入输出与状态机的输入输出就可以被“看”成是等价的。这样就只剩下了中间的计算过程了。即只要两者分别的两个过程:“计算”与“演绎”是等价的,那么FP与状态机就可以被看成是等价的。

那么,这两个过程到底等价吗?继续分析。

演绎模型因为使用的是状态模拟方法,所以非常适应人类的认识规律。所以它的应用面很广。

但对于计算模型来讲,有一个很大的问题便是:

我们并不知道所有的算法。

如多线程并发活动,多主体建模,以及不可测系统建模(决定论!=可预测论。所以计算并不总是可行或有效的)。在这些领域,要想建立一个正确的“计算”系统,要么很困难,要么基本就是不可能。

这么看的话,演绎模型的能力显然是要大于计算模型的。但由于两者的界限其实并不是那么清楚,所以这一点也很难成立。虽然已经基本上成立,但是很难完全成立。

因为如上面讨论过的,演绎型系统中其实也是存在着计算过程的,而计算型系统通过不断的输入输出同样可以达到“演绎”的目的。也就是说,如果只是单纯论“能”与“不能”,而不是多么“能”与多么“不能”的话,那么两者其实还是可以勉强算作等价的。但只单纯论能与不能,显然不是我们评价事物的标准。最少不是我们采纳一种技术的标准。

因为“能力”本来就是相对的,指的是要考察的事物完成特定或一定范围工作的能力。而如果讨论的是是计算型的应用,显然计算型系统的能力会更强一点,特别是经过特别软硬件设计的计算型系统;如果是演绎型的应用,显然演绎型系统的能力要强得多。特别是在那种多主体演绎系统中,计算型系统根本就是捉襟见肘。意思是在这种情况下,即使强制使用了计算型的系统并且最终设计出可能工作的系统,也很可能是一个在质量与维护上都存在重大问题的系统。

不过前提是一定要对自己的应用目标进行详细的分析。因为只有这样才能真正搞清楚自己的应用属于哪种类型!

另外就是,这里的计算指的当然不只是数学计算。它其实包括大部分我们平时所开发的系统(这也是为什么我们叫现在的电脑“计算机”的原因?)。平常企业级开发中经常遇到的也是这种系统。演绎型应用应该大多只存在于科研领域或一些特殊应用中。

这就引出了一个很大的问题。因为这与我们的经验截然不同。难道我们一直在做错误的事情吗?因为我们一直都是在在演绎型系统上或使用演绎型方法(使用命令式语言)进行应用的开发吗?难道这么多年我们一直被蒙在鼓里:我们根本不需要写那么多的变量?事情完全可以变得更简单?那么OO算怎么回事?

首先,大量的“变”量肯定是不必要的。除了不必要,而且还可能引起很多的错误。因为状态越多,出错的可能性就越大。这个在很多高并发的系统中已经得到了验证;

其次,对于大多数(企业级)开发工作来讲,系统要进入的状态我们都是提前就知道的,并不需要通过状态演绎来达到求解未知状态的目的------因为没有这样的问题,当然就没有求解这种问题的需要了。其中最主要的开发工作是数据处理,属于“计算”型任务,而不是“演绎”型任务。最少,在这些外部任务中很少会存在状态“演绎”类的需求。所有的状态演绎,其实经常是我们自己搞出来的,是不必要的。所以,是的。事情的确可以变得更简单。因为我们本来就只有“计算”的任务,而“计算”型的工作本身比起状态管理显然要简单得多。

最后,其实OO与“计算”型系统并不矛盾。因为作为一种数据封装或建模手段,在计算型应用中,OO仍然是有它的一席之地的。只不过在使用OO的时候,不要大过于夸大它的作用或把它放在太重要的地位就可以了。让OO完成与对象相关的事情,更多的事情让函数去完成就可以了。

 

 

计算型系统的特征:工作在一个相对静态的语义环境中。

利用对输入的“冻结”,模拟状态机系统中的初始状态。

即在这种系统中,“状态”只在两个地方存在:一个是输入,一个是输出。两者中间则是计算过程。因为它没有变量,所以基本上所有的“计算”结果都只能放在一个地方,即函数的输出中。这样的输出与前面的输入,我们可以把它们看作状态。把多个这样的输入输出过程联系起来(利用连续的函数调用),就可以得到一连串的“状态”(因为互相连接的输出与输入其实是同一个值,所以每次连接只能算作一个“状态”)。这样,我们就可以在“计算”机中得到与“演绎”机中相同的状态演绎能力。并且,如果此时我们再把演绎机中的每一个演绎步骤与计算机中的函数等同起来,我们就“得”到了两部一样的机器。我们叫它们什么都可以。计算机,演绎机都可以。

唯一的区别是,两者在两件事情上各有所长。

“计算”机因为强调或最初的问题域就是计算,所以在计算这一块有许多非常优秀的思想和办法。并且因为强调的不是状态演绎,所以在状态演绎这一块显然考虑很少或者说基本上没有。毕竟通过函数堆栈或尾递归进行状态演绎不是一件容易的事。

而“演绎”机所面对的问题则是计算。FP中的LAMBDA算子,高阶函数和柯里化等显然都是对于分解计算非常有效的办法。而演绎机所使用的只是最简单的自然演绎证明法,不管是从效率上还是思维方式上,与LAMBDA,高阶函数,柯里化等“科学”计算手段相比,怎么着都透着一股土气。为什么这么说呢?前文提过的,“演绎”机其实是一种“证明论”系统。而“计算”机则不同,它因为当初的动机就是计算,所以它更倾向于使用那些已经被从逻辑上证明是正确的,抽象程度更高的以及更适合于学术或工程计算的手段去完成计算。它的目标是计算,不是证明。

这种区别,给我们的技术选型又增加了一重矛盾:

从需求的角度考虑,我们的工作显然更倾向于计算而非演绎。因为前面提到过的,我们平常所面对的工作大部分都非这类系统。因为我们并不会经常去开发一些需要状态演绎或证明的系统;

但是从技术本身的角度考虑,“演绎”机提供的自然演绎手段显然更符合我们平常的的思维习惯(这正是它为什么流行这么多年的根本原因------很多人总是在讲,我们一直在用错误的方法思考。我觉得正误是相对的。判断事情不能一刀切。一件事实,它既然能够存在肯定会有它的理由------现在不是就找到了吗?)。相反,“计算”型系统提供的那些手段虽然很高级(高效率,高抽象,高扩展性------算子,高阶函数,柯里化这些东西在整个算法世界中,只是一些最基础的元素),但“书山有路勤为径,学海无涯苦作舟---此句被普遍误读。具体如何被误读及真实意思,在理解上因人而异。所以这里不讨论”,这些东西如果那么容易理解,为什么这么多年过去了还是流行不起来呢?

这样,可能会有人想,我如果使用FP编程,但不用那些高深的算法,这样行不行呢?

行。也不行。

因为如上所述,虽然我们平时的工作大部分都是些“计算”型的工作,但我们的思维习惯却是倾向于“(自然)演绎”型的。也就是,对于我们这些天性愚钝的大部分人------或凡夫俗子------来说,在税收与死亡之外其实还有两件事情是非常确定的:第一,我们需要钱;第二,我们很愚蠢。所以,我们会接下任何我们能搞定(或不能搞定)的工作,不管通过任何手段------哪怕它看起来真的很愚蠢! 直到有人用更低的价格或更好的服务把我们的工作给抢走。就象汽车抢走马的工作,或e-mail抢走邮差的工作一样。

这样一分析,结论就变得比较清晰了。显然,FP的效率更高,但IP更符合我们平常的思维习惯。尽管大部分工作都是计算型的任务,但工作是什么由客户说了算,但工作怎么做却是由“我”说了算。

客观上,FP与IP不分胜负。因为两者的应用环境截然不同。FP的技术方向是偏向于通过分解计算过程的办法来解决复杂计算或提高编写计算型程序效率的问题。而IP的方向则跟“计算”没有任何关系。或者说,它甚至不尝试去解决任何问题,只是简单地提供一种“写”程序的工具或语言而已。

所以,也可以说IP是一门程序员导向的语言(在编程领域,难道这不是一件值得庆幸的事吗?语言的客户本来就绝大部分都是我们这些凡夫俗子------从这个角度讲,IP的效率其实比FP的效率高多了-----人多,基数大),而FP则是一门问题导向的语言(很多函数式语言都会对纯函数式语言进行相应的扩展,以适应相关领域的编程。erLang是其中的一个例子。------为什么要扩展语言呢?为什么不把那些想要的特性做成库呢?------什么是语言?什么是库?库跟语言的分界应该在哪里?语言应该做到什么?库应该做到什么?各自承担的功能应该是怎么样的?------语言学中肯定有相关的结论!------为什么我会自己去研究这个问题而不是从语言学中去借鉴方法或经验呢?为什么要浪费自己的时间?)。

总之,你敢用FP做项目,请首先保证10年以后现在的程序员仍然在岗。

其实就算只从可理解性的角度看,这样的讨论也是完全不必要的。因为即使FP能力再强,其能力也不一定需要暴露出来的。试想,谁会设计一台必须懂十六进制计数法才能使用的计算机?

程序员接口应该被专门设计,以适应程序员的思维习惯。而不是由语言内在的任何特性影响如并行,分布式,容错等ERLANG极力宣传的那些东西。也就是说,在设计一门语言时,语言的语法设计跟特性或能力设计应该是分开的。比如,无论能力如何,任何特性的加入都不应该影响语言的易理解性?ERLANG最初是跑在电信的大型交换机上。这些机器是非常特殊的应用。即使它们在某些方面跟我们平时所开发的应用相同,比如共同拥有高并发,分布式,容错等需求,但这并不是你语言本身可以没有易理解性的借口。高并发,分布式,容错,并行,这些东西,如ERLANG自己讲的,别的语言都是可以实现并拥有的。ERLANG真正的优势是在有一个高可靠性,经过现实检验的库。

再把东西打散,重新整理一下。比如,把我们需要的语言的特性分别列出来:

x,y,z......

这不是开玩笑。因为我敢打赌,对于99%的应用,我们希望其中的第一个元素(x)的结果应该是“易理解性”。易理解性包括什么呢?它包括,比如至少,地球语而不是火星语;。。。。。。

象内存事务(包括STM,HTM),虽然好象最近没有成功的希望,但这是一个良好的工作模式。即首先把东西给提出来,然后再想办法把它给藏回去。对外只展露需要展露的东西。

永远记住:我们要的是特性。是x,y,z。你要选择什么语言,把你要的东西列出来并且分一下类:哪些是必需的,哪些是不必需的,哪些是prefer的,哪些是不需要的,哪些是有害的。然后把候选语言(s)的特性全列出来(最好使用两份表:一份厂商的;一份目前用户的)。再拿着这些表跟你的需要做一下match,然后根据重要程度按权值计分。最后取那个最高分就可以了。不过当然,这是假设程序员的语言能力在不被考虑的情况。除了这个,其余的事情都是没有意义的。完全没有意义。如整天跟在人家后面起哄,讲这个好,那个好。因为好与坏本来就是相对需求而言的。需求不提出来,你老盯着语言本身瞧来瞧去,是得不到什么结论的。

因为你的工作并不是在这里结束的。

你的工作是:观察,然后做出最优的选择。

记住:你不是语言评论家(那谁是?------别担心,肯定有人是)!

所以,此文到这里结束(我是中间穿插写的)。转来转去,还是得回瓜哇城。

芬兰人已经在手机市场上输给了美国人。瑞典人这次恐怕是要步芬兰人的后尘了。。。。。。

(此文在这里结束。后面的不用看了!)

 

真正的“计算”机。

都是计算机,只是强调的阶段不一样。“演绎”机强调演绎,“计算”机强调计算。具体的编程方法或语言、系统,是根据需要来定制的。 

 

结语:

世上本没有路。走的人多了,也就成了路。所谓FP,IP都是完全主观的概念,并不是真实存在的东西。而从头到尾真正存在的东西,其实是组成所谓“FP”或“IP”的那些思想与方法。如,状态演绎法及实现演绎的手段------变量,对象,共享状态等,和计算法及实现计算的手段------函数,高阶函数,返回值,纯抽象输入输出(纯抽象指的是,输入输出的已经不是任何事物的状态。只是在实际应用的时候,我们把它想成某个事物的状态------这个是计算的前提------否则整个计算就是毫无意义的,与现实脱节的------直到它重新被与实际的事物联系起来,否则它不具有任何哲学上的意义------即从认识观的角度讲,其计算结果对我们没有任何帮助),以及两者共同拥有或使用的一些方法如延续(这个在哪都是需要的。除非程序是非组合的。所以即使是IP也是包含这个能力的------CPU天生具有这种能力------如指令地址自动加1或条件跳转等原生处理器行为)。

离开这些组合因子而单纯去考虑FP好还是IP好,都是没有意义的。名字本身本来就是没有意义的。 恰恰相反,如果跳出名字的约束而单纯从状态演绎或计算的角度去思考一种语言的话,会很容易发现,其实两者冲突并不大。两者只不过各有所长而已。如,通过设计多个函数,使用FP一样可以做到状态演绎的目的;而通过状态演绎,也是同样可以达到计算的目的的(比如一个问题:一个人一小时跑10公里,问他两小时跑多远?------这样的问题很显然是可以通过状态演绎完成的。虽然比较傻,但首先它代表的是一种不同的思路或方法。其次它代表了“演绎”机与“计算”机的能力相通的地方)。

知道了FP与IP的演绎与计算的本质,再回过头考虑一下演绎与计算两种方法在实际工作与生活中的作用,发现演绎其实最大的作用在于求解规律,而计算则在于利用已经知道的规律去求解结果(为什么科学家喜欢FP,因为他们知道很多的规律------或者至少------他们以为他们知道很多的规律)。两者非但不矛盾,反而是一对完美的“求解”工具。比如现在随着计算技术的发展,很多领域开始采用多主体建模的方法进行现实虚拟,以求解事物的规律。而关于科学计算对函数的依赖,就更不用提了------他们在没有计算机的时代就已经开始了使用真正的数学函数进行计算。先求解规律,再根据得到的规律进行计算,是一种再正常不过的探索方式。哪里来的矛盾?所以说,在讨论或认识事物的时候,切忌一刀切!

“白猫,黑猫,抓到耗子就是好猫”!大多伟人身上,除掉胆识与气魄,最宝贵的就是那种实事求是的精神。的确,不为表象所迷惑,才是一种真正正确的学习与研究态度。

所以,一个应用到底要采用哪种语言关键还是决定于要求解的到底是什么东西?是规律还是结果。我相信对于大多数编程语言的使用者来说,使用一门语言的目的还是使用它进行相关的计算(或处理------关于“什么是计算”或“计算是什么”的问题,其实很简单。只要想想“一切都是函数”这句话,就什么都明白了),而不是用它去求解规律。毕竟我们不可能个个都是搞研究的。

续(2010-12-15):

前面的讨论结果有疏忽。

经过再思考,发现状态演绎在平常的应用开发中需求并不少。特别是在WEB应用中。因为一个个的 

你可能感兴趣的:(函数式编程,函数式语言,命令式语言,变量不可变)