为什么函数式编程没有流行起来?

为什么函数式编程没有流行起来?,第1张

但是对于很多编写应用程序的编程人员来说,函数式编程仍显得那么陌生和遥远。对此Confusion在tweakblognet上发起了一场关于“为什么函数式编程语言没有流行起来”的大讨论。Confusion认为函数式编程语言之所以没有流行起来,原因是其文档及示例都显得过于复杂难懂,让人望而却步:软件工程领域的一些权威对于函数编程(functional programming——FP)事实上不怎么流行深表遗憾。支持者这一观点的人通常责怪听众短视或其老板无能,认为不同意他们意见的人根本不懂函数式编程为何物。而这反过来却可能说明一点:他们没有给出恰当的解释。我想限制FP语言被采用的实际原因是:真正吸引大家的是函数式编程语言的一些优点,但是这些优点并没有被很好地阐述和示例。同样,这些解释和样例在说服软件工程师方面显得非常失败,因为它们没有回答软件工程师们在日常工作中所关心的问题。 接着Confusion用一个有关定义reduce的例子进一步说明了这一点:(reduce f x) nil = x(reduce f x) (cons a l) = f a ((reduce f x) l)……这很抽象,多数人并不熟悉。可是,FP语言的文档只包含了这类非常数学化的例子。难道没有人写一个用来证明FP强大功能的宠物店的例子吗?这才是我们大多数人的常规工作。 对于Confusion的这一看法,引来了众多读者的讨论,大家就这一问题纷纷发表了自己的看法。 RayNbow对于FP例子过于抽象的看法就表达了不同意见:斐波纳契数列(Fibonacci )的例子(还有阶乘)通常被用作说明语言语法的具体例子。因为函数式编程语言以函数为中心,还有什么更好的方法展示如何定义它们的么?你可以把斐波纳契数列和阶乘的例子看作函数世界的Hello World。 Tubbie认为FP的特长就在于计算方面,因此编写数据库或用户界面应用的程序员根本不需要使用它:FP支持者之所以展示计算例子,是因为FP擅长此道。问题是大多数程序员每天编写的大多数代码都是关于与数据库、用户界面等交互的,并不包含复杂的数学运算或算法。FP语言是非常不同的的语言,如果只解决边界条件问题,就不需要学习它。 Dooievriend非常赞同Tubbie的说法:……我很喜欢函数式语言,它非常适合解决要解决复杂的运算问题。……但是关于用它来构建GUI并访问数据库,我没有任何思路。对我来说,只在复杂和经常变化的问题出现时,才会用到FP,但它是嵌入到其他语言中使用。这是FP语言应被使用的方式,我至少知道Mercury是这么用的…… 有不少人表示同意Dooievriend的这一说法,他们认为只有混合了命令式语言(imperative languages )与函数式语言(Ffunctional languages )才能做到两全其美。就函数式语言没有很好的说明和例子这一说法,虽然Morton也表示同意,但是他并不认为FP没有流行起来:……我没觉得FP不流行。我经常发现同事在代码中使用了函数模式,可是他们自己都没有意识到。现在也很难找到不支持FP的语言。在Java和Net都开始支持FP概念的时候,你们居然还担心FP不流行。 Alex更是现身说法,说自己已经把函数式编程当作了“秘密武器”:你的意思是还没人写出一篇能让人信服的说明函数编程好处的文章吗? …… 我和我的团队已经用F#开发了一个稳定的、现实的、商用的业务应用,而且已经被大公司买走了。函数式编程最成功之处是,根据任务的特性,代码量可以减少4到10倍,对此我十分确信,因为我们用F#重写了一些老代码,所写的代码也更加易于维护和扩展。 因此,它给了我们竞争优势。或许说服不了大多数人对我们来说是好事:),我们已经拥有这一秘密武器了。 虽然大家看法各不相同,但是有一点可以看得出来,函数式编程特长和优点是大家有目共睹的。

形而上的思维: 1、数据不可变的思维:let a = 100,意义不是把100赋值给变量a,而是把a符号绑定(或者叫匹配)到100。 2、一切皆表达式思维:if b then 100 else 10,这不是条件跳转,而是一个三元表达式。 3、函数是第一类值:函数可以作为参数传输,也可以作为结果返回,更可以由一个函数演化成另一个函数。 形而下的思维: 1、用递归替换循环。 2、难以尾递归的时候考虑使用延续函数(continuation)。 3、高阶函数、部分应用、Lambda演算。 4、用泛型、接口、可区别联合类型替换类继承。 5、用二叉树替换普通链表后可以支持高并发计算。 =================================================== 这些也只是feature而不是思维。我想知道的是这些feature之后的逻辑。 ----------------------------------------------------------------------------------------------------- 再往上说就不接地气了,先从函数式语言说起,函数式语言其实就是模仿人的数学思维而发明的朴素,后来因为离机器太远,不容易优化而被诟病。但科技发展到今天,编译器的优化能力已经很强,软件系统越来越复杂,人的分工越来越细,函数式语言离数学更近,离机器更远,反而成为一种优势,有助于人把问题清晰化。从这个层面看,函数式编程是一种什么思维,就是推离机器的数学思维。这里没有内存、寄存器的想法,在 a=1之后,a 就不可能再等于2,当然你可以在 let a = 1 之后,再 let a = 2,但是这个a 就已经不是那个a,在停留在有内存概念的编程世界里,a 一直是 a,它是装东西的桶或者盒子,只是每次里面装的东西不同。 那么,总的来说,是先有朴素的函数式语言,然后才有今天发现函数式编程的好处, 启用了函数式语言的某些 feature,目的是为了把问题解构成更小的粒度。所以这些feature背后没什么逻辑,就好像问这石头为什么长这样一样。我只能打句偈语:本来就这样。

函数响应式编程(Functional Reactive Programming:FRP)是一种和事件流有关的编程方式,其角度类似EventSoucing,关注导致状态值改变的行为事件,一系列事件组成了事件流。

FRP是更加有效率地处理事件流,而无需显式去管理状态。

具体来说,FRP包括两个核心观点:

1事件流,离散事件序列

2属性properties, 代表模型连续的值。

一系列事件是导致属性值发生变化的原因。FRP非常类似于GOF的观察者模式。

为什么需要FRP

FRP的需求来源于对于多个值发生改变,以javascript为例子,如下:

var a = function (b,c) { return b + c } // a = b + c

其中a实际代表b与c之和,如果b或c持续不断在被改变,如何触发a值也跟着变化呢?

也就是说,上述代码只是一种表达式,并没有指定a值的变化依赖b和c。

使用Reactivejs可以达到指定这种依赖关系,代码如下:

/A = B + C

    var reactiveA = $R(function (b, c) { return b + c });

    var reactiveB = $Rstate(2);

    var reactiveC = $Rstate(1);

    reactiveAbindTo(reactiveB, reactiveC);

    reactiveA();   //-> 3

    reactiveB(5);  //Set reactiveB to 5

    reactiveC(10); //Set reactiveC to 10

    reactiveA();   //-> 15

我们将b和c可以看成是被观察者,而a作为观察者,如果随着时间推移,b和c的值不断变化,如何将这种变化传导到a呢?

我们将导致b和c被观察者发生变化的一系列事件组成事件流,可以用集合来表达事件流,那么FRP框架所要做的就是,遍历这个事件流集合,将导致b和c的变化的事件重新播放,获得a的一系列值结果。

事件流被称为被观察者序列(observable sequences),其实被观察者是一种Monads。

首先,比较被公认接受的,对于具体的语言来说,是指有一个“一等函数”(first class function)。不过函数这个概念本身就比较混乱。只能具体举例。比如,C++的函数就不是一等的,因为对象类型的值能被传参能被返回,函数类型的值就不行(会退化为函数指针)。换句话说C++的内建的“函数”是阉割的。这类函数式语言一开始以Lisp为代表,基本的理论基础是λ演算。

其次,关于表达式和语句。语句一开始就是Fortran的语法。这种设计其实没有必要,因为脱离更基本的表达式语法,也没法表示计算。Lisp就没有在语法上特意区分出语句。函数式语言的语法设计习惯上不使用语句,因为具有一等函数的表达式的表达能力通常已经足够强。C这样的语言的“语句”除了表达确定的求值顺序外,也就是一种无谓地指令式风格的习惯罢了。

第三,关于存储可变状态。修改状态(典型地,赋值)的程序是指令式范式的。函数式的范式通常避免无谓地修改状态。

极端的情况是语义规则不提供修改状态的接口,如Haskell以及C++的模板元编程,这样的风格称为所谓的“纯函数式”。

纯函数式的“函数”不具有修改值这类副作用(对于C/C++来说,读volatile对象也是副作用),所以对于确定的输入可以保证有确定的输出,且求值时函数调用可以以任意次序被整个替换——非纯函数式因为副作用顺序的不同会导致程序行为的不同。这种性质被称为引用透明性(referential transparency)。

但是注意,纯函数式并不是实现引用透明的必要条件,保证内部状态的修改不影响参数和返回值的关联同样能做到。考虑到和通用需求的差异造成的困难,对于自律的用户来说,纯函数式其实没什么必要,到头来只是一种限制而已。

纯函数唯一的输入是它的参数,唯一的输出是它的返回值。如果你以前从未接触过这个概念,你会以为所有的函数都是纯正的。毕竟,所有的函数都是接受一个或多个输入值,返回一个输出值。但在某些传统编程中,经常会有一些外来的信息流入或流出函数。例如,一个不规范的函数有可能会依赖一个全局变量或一些类成员数据。在这种情况下,函数的行为并不完全决定于它的参数值。相似的,一个不规范的函数有可能会更改一个全局变量或修改数据库。这种情况下,函数除了返回值外,还会附带一些额外 *** 作。你可以用任何语言写出纯函数,只是有些语言容易写,有些语言写起来比较复杂。例如,没有人会把Fortran当作一种函数式语言,但有些人(M J D Powell)却强制自己在Fortran里要写纯函数。你可以一直使用纯函数。但如果你想把一个值放到数据库里,光通过纯函数是实现不了的。或者当你想调用一个随机数发生器时,你可不想它保持亲系透彻性每次都返回相同的值。但是,在可以用到纯函数的时候,你应该使用纯函数,用纯函数来消除越界联系。完全的纯函数程序是不现实的;有人建议说最佳的纯度系数应该是 85% 。那么,为什么程序员不大量的使用纯函数呢?一个原因是,纯函数需要更长的参数表。在面向对象的编程语言里,对象可以隐式的依赖对象状态来减少参数数量。对于这更简洁的方法接口,你付出的代价是,你无法只通过方法本身来理解这个方法。调用这个方法时你还需要知道对象的状态。为了获得更短的方法接口而放弃亲系透彻性值不值得?这依赖于你的上下文环境和你的风格,按我的观点,我更愿意用更长的函数接口来换取更纯的函数。为了效率,你也可以制造一些假纯度。例如,Mike Swaim最近在一个评论里给出了一个如何利用Memoization让程序的速度提升数个等级的例子。(Memoization是一种缓存技术。当一个函数向系统请求计算某些东西时,它首先看看这个东西是否已经被缓存过。如果是,它会从从缓存里取出结果返回。如果否,它会计算它,然后把输出放到缓存里。)使用Memoization技术的函数严格的说不是纯函数它的计算 *** 作直接受缓存状态的影响但这样的函数仍然保持亲系透彻性,如果你给它相同的输入,它总会产生相同的输出。

函数是个程序模块 有一定功能,有3个属性 :返回值,名字,接受参数; 例如 : int X2(int) 意思是返回一个int值,接收一个int值,假设函数功能是让值翻一倍。 你可以 用 num=x2(5) 这样 num就会被赋予10;

编程语言主要有四种类型

声明式编程:专注于”做什么”而不是”如何去做”。在更高层面写代码,更关心的是目标,而不是底层算法实现的过程。 

ex: css, 正则表达式,sql 语句,html, xml…

命令式编程(过程式编程) : 专注于”如何去做”,这样不管”做什么”,都会按照你的命令去做。解决某一问题的具体算法实现。

函数式编程:把运算过程尽量写成一系列嵌套的函数调用。 

函数式编程强调没有”副作用”,意味着函数要保持独立,所有功能就是返回一个新的值,没有其他行为,尤其是不得修改外部变量的值。 

所谓”副作用”(side effect),指的是函数内部与外部互动(最典型的情况,就是修改全局变量的值),产生运算以外的其他结果。

函数式编程具有五个鲜明的特点。

1、函数是"第一等公民"

所谓"第一等公民"(first class),指的是函数与其他数据类型一样,处于平等地位,可以赋值给其他变量,也可以作为参数,传入另一个函数,或者作为别的函数的返回值。

2、只用"表达式",不用"语句"

"表达式"(expression)是一个单纯的运算过程,总是有返回值;"语句"(statement)是执行某种 *** 作,没有返回值。函数式编程要求,只使用表达式,不使用语句。也就是说,每一步都是单纯的运算,而且都有返回值。

3、没有"副作用"

所谓"副作用"(side effect),指的是函数内部与外部互动(最典型的情况,就是修改全局变量的值),产生运算以外的其他结果。

4、不修改状态

上一点已经提到,函数式编程只是返回新的值,不修改系统变量。因此,不修改变量,也是它的一个重要特点。

5、引用透明性

函数程序通常还加强引用透明性,即如果提供同样的输入,那么函数总是返回同样的结果。就是说,表达式的值不依赖于可以改变值的全局状态。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/12156627.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-05-21
下一篇 2023-05-21

发表评论

登录后才能评论

评论列表(0条)

保存