为什么这两年javascript函数式编程程又火起来了

为什么函数式编程在Java中很危险?
发表于 16:34|
作者夏梦竹
摘要:函数式编程这个不温不火的语言由来已久。有人说,这一年它会很火,尽管它很难,这也正是你需要学习的理由。那么,为什么函数式编程在Java中很危险呢?也许这个疑问普遍存在于很多程序员的脑中,作者Elliotte对此发表了一些见解,我们一起来看看他是怎么说的。
在我的日常工作中,我身边的开发者大多是毕业于CS编程顶级院校比如MIT、CMU以及Chicago,他们初次涉及的语言是Haskell、Scheme及Lisp。他们认为函数式编程是一种自然的、直观的、美丽的且高效的编程样式。但奇怪的是,我和我的同事并没有为Haskell、Scheme、Lisp、Clojure、Scala而编程,这个行业里的绝大部分人都会使用Python、 Ruby、Java或C#等编程,因为它们用起来比较顺手。但在Java中,函数式编程却是低效且危险的。
为什么函数式编程在Java中很危险呢?
每隔几个月,我都会在调试中发现问题,究其原因最终可追溯到滥用函数的想法以及编程算法,更重要的原因是这个虚拟机无法创建这种编程样式。
最近想出一个很好的例子并说明了原因。Clojure (一个真正的函数式编程)返回到25整数列表:
(take&25&(squares-of&(integers)))&
此代码运行和响应速度都很快,输出结果:
(1&4&9&16&25&36&49&64&&&576&625)&
现在,假设我们想要在Java中重写,如果我们以Gosling的方式来编写Java,那么该代码是简单、快速且明显的:
for&(int&i=1;&i=25;&i++) &System.out.println(i*i); &}&
但是,现在假设我们让它变得多功能性,在特定的假设范围内重置上面的Clojure样式:
尝试运行吧,OK,从堆转储(Heap Dump)中恢复 ?
Exception&in&thread&&main&&java.lang.OutOfMemoryError:&Java&heap&space &at&java.util.Arrays.copyOf(Arrays.java:2760) &at&java.util.Arrays.copyOf(Arrays.java:2734) &at&java.util.ArrayList.ensureCapacity(ArrayList.java:167) &at&java.util.ArrayList.add(ArrayList.java:351) &at&Take25.integers(Take25.java:30) &at&Take25.main(Take25.java:9)&
当Java输出后,Clojure如何处理函数,使该函数可返回到每一个int?
Clojure如同所有真正的函数语言(与Java不同)具备懒散赋值特性。它(指clojure)不会计算不被使用的值。它可以远离这个,因为Clojure不像Java,它是真正函数式语言,可以假定变量不发生变异,使求值的顺序变得无关紧要。因此,Clojure可以执行优化,但是Java编译器却不能&&这就是为什么函数式编程在Java中是危险的原因。因为,Java不是真正的函数式语言,JIT和javac无法像在一个真正的函数式语言中积极且有效地优化函数构造对象,比如返回无穷个列表的标准函数计算,都是Java程序的死穴。这也是为什么函数式编程在Java中危险的原因。
这里,也许你会反对我的观点,OK,你无须在Java中返回所有的整数列表(或者甚至是所有的ints);但是相信没人做到这一点。
我们来一起看看比较现实的做法。这里我再次使用递归来计算而不是循环:
public&class&Squares&{ &&public&static&void&main(String&args[])&{ &squareAndPrint(1,&Integer.parseInt(args[0])); &} &&public&static&void&squareAndPrint(int&n,&int&max)&{ &System.out.println(n&*&n); &if&(max&&n)&{ &squareAndPrint(n&+&1,&max); &} &} &&}&
开始运行!
很抱歉,堆栈溢出。这就是为什么在XOM中我小心翼翼地使用循环,即使递归的地方十分清楚也不使用递归。否则,精心配置XML文档可能会造成XOM-using程序来转储核心。因此,避免在非函数式语言中进行大量递归,正如Java和C不仅仅是性能需求,也是安全方面的要求。
写在最后:
我不是说函数式编程不好,也不是说函数式编程低效,其实,我热爱函数式编程。就像我的同事认为函数式编程是自然、直观且美丽的编程风格,但当它作为一们语言比如为Haskell重新设计时,Java中函数语句的性能Bug绝对能要了你的命。
英文出自:
推荐阅读相关主题:
CSDN官方微信
扫描二维码,向CSDN吐槽
微信号:CSDNnews
相关热门文章纯函数式编程的缺点
本文总结了的几大缺点,其中主要焦点是可变性状态Mutation是否应该是默认,union-find算法的Dr. Harrop说:目前我们还没有发现一个有效率的纯函数的union-find集合。也就是说:对于有状态的操作命令式操作会比声明式操作更有效率。纯函数编程的缺点有: 1.没有纯粹的函数式的非排序的字典或集合Set自从上世纪90年代字典在软件中应用以来已经到达高峰,字典是一个每个程序员都能在标准库中常用的集合。纯函数或持久数据结构,比如那些在被发现的事物都能成为伟大的工具,他们提供了在不用担心可变状态Mutation的情况下,能重用旧集合版本实现的持久化功能,在大多数情况下(特别是逻辑编程和编译程序)。它们会使得解决方案更简洁和清晰。 部分原因是它使得回溯变得平常,然而,持久成为性能方面的一个很大成本,也就是持久化性能很差: 纯函数字典通常比一个正常的哈希表慢10倍以上,也曾经发现慢过40倍。此外,大多数语言(OCaml,Haskell、Scala)都不能表达一个快速的通用可变的哈希表,因为他们缺乏杀手锏:具体化的泛型、值类型和快速GC写屏障(write barrier)。当心,有人试图声称,Haskell的纯函数字典比Haskell的可变的哈希表更快。正确的结论是Haskell的可变哈希表相比其他语言的实现是缓慢的,所以,显得纯函数字典比较快。2.
没有纯函数式弱哈希表使用垃圾回收机制收集命令式语言,一个图的顶点与边之间的关系可以使用弱哈希表表达,垃圾回收机制会替你收集其子图。而因为在纯函数编程中,没有纯函数弱哈希表,所以,你必须自己编写垃圾回收机制。请注意,大多数开发人员从来没有用过弱哈希表,因此让他们编写自己的垃圾回收机制是多大的一个问题。3. 没有纯函数的集合根据定义,不可变集合不能支持可变状态操作,因此,如果你想共享一个可变状态的集合,比如内存数据库等,在这点上,没有有效的纯函数式解决方案可替换。4.大多数图算法看起来很差,当以FP风格编写时更慢。纯是解决某些问题的伟大工具,但是对于图算法这点,纯函数解决方案在速度和清晰都方面都很差。比较和. 为什么Haskell使用Prim的算法? 可能因为Kruskal的算法是基于union-find集合建立,而目前还没有发现有效率的纯函数的union-find 集合. 5.
传统命令式数据结构和算法的惯性是巨大的除了图算法,计算机科学65年发表的文献几乎完全集中在命令式的解决方案,因此命令式程序员会很容易站在巨人肩膀上,而纯函数程序员只能从零开始,记得几年前,Haskell还是博士论文题目。几个haskell程序员编写了通用的并行快速排序Haskell程序, 6.
所有的实现,包括纯与不纯的,都会产生太多的分配设计。1960左右,麦卡锡发明了Lisp。核心数据是结构链表。每一个列表节点是一个独立的堆分配的块。所有现代的函数性语言都是由此演变而来。在上世纪70年代Schema作为Lisp相同的数据表示策略。在上世纪80年代,SML增加了unboxing with tuple,堆分配作为一个单一的内存块。在上世纪90年代,OCaml稍微加了点unboxing的浮点数组。Haskell增加了一些unboxing数据的能力。但到目前为止,没有任何函数编程语言默认使用unboxing tuple。即使F#,基于.NET提供任意的值类型,仍然使用.NET的boxed tuple。因此,所有的现代的函数性编程语言产生很高的分配率(allocation rate)基本上没有很好的理由。因此,他们对垃圾收集器产生的压力远远超过普通必要的压力水平。这是一个严重的问题,不只是因为它使串行代码变慢,而且因为垃圾收集器是一个共享的资源,因此,对GC施加了过多压力会阻碍并行程序的可扩展性。命令式集合通常更快的,函数式语言想在性能上超过命令式集合的性能很难,前者变成后者的天花板。 7. 纯在理论上并行概念很好,但是实践中性能不行,而性能是使用并行的唯一目的。今天编写并行程序有两个目的:首先实现客观上更有效率的目的,其次使得本来很慢的方案变得不那么慢,大多数情况下,函数式编程中的并行属于后者。在高性能计算领域几乎没有人直接运行函数式代码,当大多数函数程序员使用并行编程并不是为了获得最快的性能,而是为了能在原有性能基础上有所提升。像Haskell纯函数式语言被设计成空间和时间的概念,这能让你从更高层次和视角看待你的问题,但是也产生大量内存消耗和需要很长时间获得结果。注意:人们只谈论可扩展性而否定绝对性能, 其实绝对性能和可扩展性都很重要。8.
很难解决实际问题( It took 50 years for normal people to dilute the smug weenies to the point where you can get a useful answer about functional programming on social media.)我在领域有20年,几十年来,函数式程序员和解决实际问题之间存在鸿沟,感谢这些问题因为Scala, Clojure 和 F# 等出现了改善,但是确实多年来存在蠢货主导了领域的场景,使得人们使用它很难来解决实际问题,比如一些LISP社区一直在解释Lisp一些参数为什么是好的,但是经过很多年我才发现这些参数是错误的。9.
大量有关的误传。如果你批评Haskell的哈希表性能,你会得到错误的指导,比如有人建议你关闭垃圾回收。多年来,函数式编程社区炫耀它们简短的筛选法和快速排序算法的实现,其实他们并没有真正落地执行这些算法。彻底揭露了Haskell的行业真相。原文:  英文原文:
  除非你生活中与世隔绝的深山老林里,否则你应该知道,在众多的所谓顶级编程高手()中,函数式编程是十分盛行的。也许你已经使用了某种函数式编程语言。如果你是在使用很传统的编程语言,例如 Java 或 C#,你应该知道了,这些语言很快就将。就在这美丽的新世界即将来到之际,就在我们摩拳擦掌打算大干一番之前,我想,现在应该是我们暂停一下、反省一下函数式编程在我们的日常应用开发中是否合适的好时机。
  什么是函数式编程?简单的回答:一切都是。函数式编程语言里也可以有对象,但通常这些对象都是恒定不变的 && 要么是函数参数,要什么是函数返回值。函数式编程语言里没有 for/next 循环,因为这些逻辑意味着有状态的改变。相替代的是,这种循环逻辑在函数式编程语言里是通过递归、把函数当成参数传递的方式实现的。
  为什么要使用函数式编程
  拥护者说函数式编程能开发出更高效的软件,而反对者说反之亦然。我感觉双方的观点都有问题。我可以轻松的证明函数式编程能使你更难写出针对编译器优化的代码,或者相较于传统语言的代码,对于函数式代码会编译出更慢的程序。语法跟底层的计算机硬件指令间有着很相似的对应关系,但函数式编程语言却没有这种特征。结果就是,编译器处理函数式编程语言时更费力。
  然而,优秀的编译器能把函数式编程中的闭包、tail 调用、或 lambda 表达式转换成跟传统语言中 loop 循环或其它表达式等效的代码。这需要多做一些工作。如果你在寻找一本厚达 1600 页的关于这方面的好书,我推荐你《Optimizing Compilers for Modern Architectures: A Dependence-based Approach》和《Advanced Compiler Design and Implementation》。或者你也可以使用 GCC 或任何具有多阶段编译功能、的编译器自己去证明这一点。
  对于为什么要使用函数式编程,这有一个更好的论据,现代的应用程序都会牵涉到多核计算机上的并行运算功能,程序状态就成了一个问题。所有的命令式语言,包括面向对象语言,在涉及多线程时,都会遇到共享对象的状态修改问题。这就是死锁、堆栈跟踪、低级处理器缓存命中率低等问题的根源。如果对象没有状态,这些问题就不存在了。
  在很多地方使用函数式编程或函数式编程语言都是非常适合的,甚至是最好的选择。对于纯函数计算,函数式编程明显的比命令式编程更合适。但对于商业软件或其它普通,你不能不说这正好要颠倒过来。就像
著名的阐述,&傻子都能写出计算机可读懂的代码。优秀的程序员写出的是人能读懂的代码。&而函数式编程写出的代码就是让人一眼望去不可读。
  几段代码就能让你知道我说的是什么意思。来自
语言的代码例子:
-module (listsort).-export ([by_length/1]).by_length (Lists) -&qsort (Lists, fun (A,B) -& A & B end).qsort ([], _)-& [];qsort ([Pivot|Rest], Smaller) -&qsort ([X || X &- Rest, Smaller (X,Pivot)], Smaller)++ [Pivot] ++qsort ([Y || Y &- Rest, not (Smaller (Y, Pivot))], Smaller).
  这个是
-- file: ch05/Prettify.hspretty width x = best 0 [x]where best col (d:ds) =case d ofEmpty -& best col dsChar c -& c : best (col + 1) dsText s -& s ++ best (col + length s) dsLine -& '\n' : best 0 dsa `Concat` b -& best col (a:b:ds)a `Union` b -& nicest col (best col (a:ds))(best col (b:ds))best _ _ = ""nicest col a b | (width - least) `fits` a = a| otherwise = bwhere least = min width col
  人 vs 机器
  一个不怎么样的程序员一般都能从一段命令式的代码中很快的看出其基本的功用 && 甚至这是一种他从未见过的语言。然而虽然你也能从一段函数式代码里分析出它的功用,但你绝对不可能简单几眼就能看出来。不像命令式代码,函数式代码并不体现出简单的语言结构。它展现的都是数学结构。
  我们的编程经历了从纸带打孔到汇编到宏汇编到C语言(高级宏汇编)再到抽象出了很多老实机器上复制运算的高等编程语言。每一步都使我们越来越接近《星际迷航4》里的场景:遇到麻烦的 Scott 对他的鼠标说出指令(&&)。数十年的进步使得编程语言越来越容易被人类阅读和理解,函数式编程的语法是在把时钟指针往后拨。
  函数式编程能解决并行运算的状态问题,但付出的代价是失去人类可读性。函数式编程也许完全可以用于任何环境开发,它甚至可以通过定义面向领域(domain-specific)的编程语言来拉近人类语言和计算机语言之间的距离。但它糟糕的语法使得它极不适合常规目的的编程开发。
  不要这么着急的判断潮流 && 特别对于那些不想有太多风险的项目。为什么大家都觉得函数式编程将会流行?
为什么大家都觉得函数式编程将会流行?
为什么大家都觉得函数式编程将会流行?
司马牵牛 编译 &< &
【51CTO精选译文】这几年来,函数式编程似乎忽然红火了起来,但事实上,函数式编程却是个已经存在了四十余年但长期只活跃于学术领域而非商业开发领域的一个编程方式。那么,为什么大家会认为函数式编程将流行?有一位从事电子商务Web应用开发的程序员在stackoverflow上提出了自己的疑问,而这个疑问也普遍存在于很多程序员的脑中。让我们先看看这位程序员是怎样说的:51CTO编辑推荐:jjnguy:不管什么时候去 stackoverflow 或 reddit 这样的网站,总会遇到一群 Haskell / Scala / F# 语言的粉丝,顽固地坚持函数式编程将是下一个“大事件”。不可否认,有一些问题的确适合使用函数式编程的方式来解决,但是我过往的经验告诉我: ◆普通的公司程序员,比如和我一起工作的大多数同事,并不懂函数式编程,并且多数工作环境也不会让你使用它来编程。 ◆实际上,大学不教函数式编程这门课(或者现在大学有这门课?)◆大多数应用程序足够简单,使用普通的面向对象方式就可以解决。函数式编程真的有必要吗?或者只不过是一个很酷的玩具?我不是想要贬低它,我只是真的不确定:如果在我的日常工作(编写电子商务 web 应用程序)中使用函数式编程的几率接近零,我是否还应该去学习这种编程?&而对函数式编程充满信心的程序员们往往不会默默认同这样的观点,而他们大多数也不是第一次遇到这样的疑问。51CTO编者选取了以下几个比较有代表性的回答进行了编译:#t#joel.neely:我觉得,对于函数式编程会流行这一话题没有任何疑问,因为作为一种编程方式,它都用了大约四十年了。面向对象的程序员,每当为了编写干净简洁的代码而使用了不可变对象时(immutable object),这都是借用了函数式编程的概念。不过,强制性的函数式编程语言最近这些日子里的确获得了很多文章的关注,而这些语言是否将主宰未来的编程语言还是一个悬而未决的问题。我自己的推测是,那种混合型、多范式的语言,比如
或 OCaml ,很可能会超过那些“纯正的”函数式语言,正如纯正的面向对象语言(Smalltalk、Beta 等):他们影响了主流编程但并未最终成为使用最多的语言。最后,我要实在忍无可忍地指出,你那些有关函数式编程的评论和不久前我从过程式编程程序员听到的论述高度相似:◆“传说中的”“普通”程序员不懂它。◆没有大范围内教授这种语言。◆用它编写的任何程序都可以使用当前的技术完成。图像用户界面和“作为商业模型的代码”这些概念让面向对象受到更多好评,同样,我相信对不可变性的使用逐步增加以及更为简单的(大规模)并行操作将让更多程序员看到函数式编程所具有的优点。虽然在过去 50 年左右的时间里(这个时间构成了数字化计算机编程的整个历史)我们经历了很多,但我认为仍然有很多东西需要学习。从今日起,二十年后,程序员回头再看,看到我们当下所用的那些工具是如此原始,其中包括现在流行的面向对象和函数式编程语言,他们将会大吃一惊。&Greg Hewgill:对我来说,主要的加分处在于它固有的并行操作性,尤其是现在更高频率的CPU正在转变为更多内核。我认为,函数式编程不会成为下一个编程范式并完全替代面向对象类型的方式。但我的确认为,如下说法非常正点:我们或者需要使用函数式语言编写某些代码,或者那些通用的语言将逐渐包含更多函数式构造。&stbuton:即使你在工作中从不会用到函数式语言,理解函数式编程将能够让你成为一个更优秀的开发人员。总的来讲,对于代码和编程,它将给予你一个新的视角。让我说,没有理由不去学它。我认为,那些能够将函数式和命令式这两种风格很好地混合在一起的语言将是最有趣的,并且最可能获得成功。&Norman Ramsey:我可以告诉你人们为什么因函数式编程而兴奋:很多很多程序员在使用函数式编程时都有一种“信仰改变”的体验:生产力提升了一倍(或者十倍),而产出的代码更加容易改写,bug也更加的少。这些人把函数式编程看作是秘密武器。一个很好的例子是Paul Graham的《》(击败平均值)。而你知道他的应用是什么?电子商务Web应用。========我们来总结一下上面这些答案。综上所述,函数式编程的优势主要针对两点:1. 不可变性(immutability)2. 并行操作性(parallelism)这两个概念其实与编程范式或编程语言无关。比如,Java也有不可变对象,只是需要通过自己编写实现方法来实现;而并行编程,近日在上频频露脸的就以为一大亮点。对于函数式编程在这两个方面的优势,我们有现成的论据可以引用。先看看这篇(这篇文章来自《》一书,而这本书的联合作者之一就是Scala创始人)。文中有这样的一句话:“不可变数据结构是函数式语言的一块基石。”而这篇《》一文中,则十分详细的描述了函数式语言天生的并行操作性:“函数式语言已经在简化并行开发中证明了它的作用, 这得益于它既不用共享内存,也不会产生副作用(side effect)的函数。”可以说,函数式编程在这两个方面上的优势是毫无疑问的。事实上,这两个优势是同进同退的,因为良好的不可变性的使用才能确保不会成为并行操作时性能的瓶颈。而这个优势的重要性,前面的答案中已经很明显的给出来了:如今的CPU在增高频率上已经遇到了瓶颈,多核CPU成为趋势,因此能够充分发挥多核性能的应用程序将越来越重要。技术领域的改变是极快的,因此,“大多数程序员不了解它”或者“没有大范围教授这种语言”都是十分无力的论证(比如说,OO编程并非从一开始就流行)。种种发展趋势都指向了函数式编程,而那些在编程范式上“兼容”的多范式编程(如Scala语言同时支持函数式和命令式的风格,虽然)将必然会流行。编者一直在推荐Scala编程语言,因为这个基于JVM的多范式编程语言无论怎么看都有很好的发展前景。如果你有Java开发的经验,那么51CTO的中收集的一系列针对Java程序员的教程文章相信一定会对你有所助益。做为程序员,没有理由不去学习任何一门编程相关的技术,更不用说那些很可能会影响到自己未来几年饭碗的技术。编者一直相信对于一位程序员,没有“与我无关的语言”或者“与我无关的编程方法”,因为编程有着共通的哲学。没有哪个优秀的程序员会自称“Java程序员”,“.NET程序员”,“PHP程序员”。他们的唯一称呼就是“程序员”。因此,如果你还没有开始学习函数式编程,那么现在就开始吧。【责任编辑: TEL:(010)】
发表评论:
TA的最新馆藏[转]&[转]&}

我要回帖

更多关于 js 函数式编程 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信