然而,匿名函数是数学和计算机科学中一个非常古老且非常著名的概念(由数学家阿隆佐·丘奇(Alonzo Church)于1936年左右发明,自1958年以来被Lisp编程语言使用,请参见此处)。
那么为什么今天的主流编程语言(其中许多起源于15到20年前)却从一开始就不支持lambda函数,而只是在后来才引入它们呢?最近几年采用匿名功能?
重要说明
这个问题的重点是在现代的,主要的-中引入匿名函数流(因此,可能有少数例外,非功能性)语言。
还请注意,匿名函数(块)存在于Smalltalk中,这不是一种功能语言,而且即使在C和Pascal之类的过程语言中,很长一段时间都存在普通命名函数。
请不要通过谈论“采用功能范式及其好处”来概括您的答案,因为这不是问题的主题。
#1 楼
函数式编程,或者至少在某些方面肯定存在明显的趋势。在某些时候采用匿名函数的一些流行语言是C ++(C ++ 11),PHP(PHP 5.3.0),C#(C#v2.0),Delphi(自2009年起),Objective C(块)而Java 8将为该语言带来对lambda的支持。而且有些流行的语言通常不被认为是功能性的,但是从一开始,或者至少在早期,受支持的匿名功能就是JavaScript。和所有趋势一样,试图寻找引发这些趋势的单个事件可能是浪费时间,通常是多种因素的组合,其中大多数是无法量化的。 《实用通用Lisp》于2005年出版,可能在将Lisp作为一种实用语言引起新的关注方面发挥了重要作用,因为相当长一段时间以来,Lisp大多是您在学术环境或非常特定的细分市场中会遇到的语言。 JavaScript的流行也许在引起人们对匿名函数的新关注中也起了重要作用,正如他在回答中所说的那样。面向功能性(或主要是功能性)语言。诸如Erlang(1986),Haskell(1990),OCaml(1996),Scala(2003),F#(2005),Clojure(2007)之类的语言,甚至R(1993)之类的领域特定语言似乎也获得了强烈的关注在介绍它们之后。总体趋势已引起对较旧的函数式语言的新关注,例如Scheme(1975)和显然的Common Lisp。
我认为最重要的事件是行业中采用函数式编程。我完全不知道为什么这种情况一直没有发生,但是在我看来,在90年代初期和中期某个时候,函数式编程开始在行业中占有一席之地,(也许)从Erlang的激增开始电信和Haskell在航空航天和硬件设计中的采用。
Joel Spolsky写了一篇非常有趣的博客文章,《 JavaSchools的危险》,在那儿,他反对当时的趋势,即大学倾向于Java而不是其他可能更难学习的语言。尽管该博客文章与函数式编程关系不大,但它确定了一个关键问题:
其中存在争论。像我这样的懒惰的CS本科生多年抱怨,再加上业界抱怨美国大学的CS专业毕业的学生很少,在过去十年中,许多其他本来很好的学校都100%废除了Java。很时髦,使用“ grep”评估简历的招聘人员似乎很喜欢,而且最重要的是,Java没有什么困难可以真正淘汰程序员,而无需动脑筋做指针或递归,辍学率较低,计算机科学系有更多的学生,预算更大,而且一切都很好。她在我大学期间。这绝对是一个苛刻的情妇,而且不是可以立即提高工作效率的语言(嗯,至少我不能)。与Lisp相比,Haskell(例如)友好得多,您无需付出太多努力就可以提高工作效率,而又不会觉得自己像个白痴,这可能也是向函数式编程过渡的重要因素。
总而言之,这是一件好事。几种多用途语言正在采用范式的概念,这些概念以前对于大多数用户而言似乎是不可思议的,并且主要范式之间的差距正在缩小。
相关问题:
函数式编程正在兴起?
为什么函数式编程在业界并不流行?现在流行吗?
为什么函数式编程还没有被接手?
进一步阅读:常见的Lisp和Lisp方言Clojure流行起来吗?
函数式编程:是什么引起了函数式编程的兴起?为什么选择Haskell?
评论
感谢您的回答(以及很多有趣的想法)。 +1然而,我认为将(仅)lambda引入编程语言是向FP迈出的很小的一步,甚至可能使许多人感到困惑(lambda在一种命令式语言中独自做什么?在学习了一些Haskell,Scala和SML之后,我不觉得我可以使用仅支持lambda的命令式语言(真正的FP)(关于currying和模式匹配,不变性?)。
–乔治
2012年11月3日,10:22
让我们继续聊天中的讨论
–乔治
2012年11月3日10:52
@YannisRizos:Perl自从5(1994)发布以来就具有匿名功能,但是直到5.004(1997)才完全“正确”。
– Blfl
2012年11月3日,11:01
@penartur我也是这样想,但是友好的编辑器通过在此处指向我来纠正了我:msdn.microsoft.com/zh-cn/library/0yw3tz5k%28v=vs.80%29.aspx
– yannis
2012年11月4日14:46
我认为,导致功能语言流行的主要“事件”是网络。更具体地说,是从桌面程序到服务器端的转变。这使开发人员可以自由选择任何编程语言。 Paul Graham和Lisp在90年代就是一个著名的例子。
–吉拉德·纳尔(Gilad Naor)
2012年11月5日下午6:59
#2 楼
我认为有趣的是,函数式编程的普及与Javascript的增长和扩散并驾齐驱。在功能性编程方面,JavaScript具有许多根本特性,在其诞生之初(1995年)在主流编程语言(C ++ / Java)中并不十分流行。它突然被作为唯一的客户端Web编程语言注入了主流。突然之间,许多程序员只需要了解Javascript,因此您就必须了解一些函数式编程语言功能。 Javascript。评论
Java语言当然是一种重要的语言,但是我不确定Java语言的引入是否可以单独说明函数式编程的流行:正如Yannis在他的回答中所说明的那样,近年来出现了许多其他函数式编程语言。 。
–乔治
2012年11月3日13:54
@ Giorgio-可能还有许多其他功能编程语言,但是(相对)没有人使用它们。 JS的使用以及越来越多的观点认为C ++ / Java编写函子的方法既痛苦又烦人,这实际上是推动主流发展的动力,即使更多的学术语言坚定了应如何实现它们。
– Telastyn
2012年11月3日14:08
通常,动态语言的流行是对Haskell流行的一种解释:book.realworldhaskell.org/read/…
– yannis
2012年11月3日14:12
同样,问题的焦点不是总体上FP的普及,而是关于通用非功能性语言中匿名功能的最新引入。即使大型公众(大多数程序员)不了解它们,语言设计师也非常了解它们。一开始一定有理由将其排除在外。也许对于90年代初期的开发者来说,它们被认为是不直观的。
–乔治
2012年11月3日14:16
@giorgio-与Java样式函子相比,实现起来要麻烦得多。结合缺乏知识/采用的知识,这是一个非常明确的设计选择。
– Telastyn
2012年11月3日14:22
#3 楼
JavaScript和DOM事件处理程序意味着数百万程序员必须至少学习一些关于一流函数的知识,才能进行网络上的任何交互。职能。因为JavaScript不会关闭this
,所以它也强烈建议您也了解闭包。然后,您变得很精明:您了解封闭了词法作用域的匿名一流函数。一旦适应了它,便会以每种使用的语言来使用它。
评论
+1不仅涉及匿名功能。闭包是一个比定义内联临时函数更广泛的概念。
– phkahler
2012年11月5日,0:16
@phkahler:您是对的,从这个意义上讲,Java已经有了闭包(甚至比使用函数文字获得的闭包更强大),但是对于单方法匿名类的常见情况它缺乏简洁的表示法。
–乔治
2012年12月4日21:21
#4 楼
当然,这不是唯一的因素,但是我要指出Ruby的流行。并不是说这比开发板上已有的六个答案中的任何一个都重要,但我认为很多事情是同时发生的,对它们全部进行枚举很有用。Ruby不是一种功能语言,当您使用过ML之类的东西时,它的lambda,prods和block似乎很笨拙,但事实是,它普及了映射的概念,并减少了一代年轻的程序员从Java和PHP转向时髦的牧场。几种语言中的Lambda似乎比其他任何东西都更具防御性(“粘住!我们也有这些!!)
但是块语法及其与.each,.map集成的方式,.reduce等使匿名函数的想法广为流行,即使它实际上是一个像协程那样的语法构造,并且通过&轻松转换为proc使其成为函数式编程的门户药物。
我辩称,编写JavaScript的Ruby on Rails程序员已经开始以一种轻量级的功能风格来做事,再加上程序员博客,Reddit的发明,黑客新闻和Stack Overflow的同时出现,这些想法得以传播在互联网上比在新闻组时代要快得多。每个人都希望使用现有的语言来防止进一步的缺陷。
评论
+1是一个很好的答案,并且(如果可能的话,因为我只有一个投票)+1指出“几种语言的Lambda似乎比其他任何东西都更具防御性(” !!)”。我认为这也是一个因素。对于某些语言来说,lambdas是一个不错的功能,即使它对整个语言几乎没有表现力,但它还是使该语言颇受欢迎(a许多程序员似乎认为对匿名函数的支持等同于完全支持函数式编程。
–乔治
2012年11月4日19:03
我真的认为这是近年来大多数语言实现块语法的原因。但是确保唯一的方法是询问语言开发人员的动机。我们只能推测imo。
– SpoBo
2012年11月5日,7:41
对我来说,Ruby是最先成为块摇滚的语言,非常吸引人,因此+1。 Haskell可能也产生了作用。
–rogerdpack
2012年11月9日17:42
#5 楼
正如Yannis所指出的,有许多因素影响了以前没有的语言对高阶函数的采用。他仅轻描淡写的重要项目之一就是多核处理器的激增,以及随之而来的对更多并行和并发处理的需求。对并行化非常友好,从而使程序员可以轻松地使用多个内核,而无需编写任何显式的线程代码。正如Giorgio所指出的,函数式编程不仅仅是高级函数。函数,加上映射/过滤器/减少编程模式,以及不变性是函数编程的核心。这些东西共同构成了强大的并行和并发编程工具。值得庆幸的是,许多语言已经支持某种不变性的概念,即使不支持,程序员也可以将它们视为不变的,从而使库和编译器可以创建和管理异步或并行操作。
语言的顺序功能是简化并发编程的重要步骤。
更新
我将添加一些更详细的示例,以解决Loki指出的问题。
请考虑以下C#代码,这些代码遍历小部件的集合,从而创建新的小部件价格列表。
List<float> widgetPrices;
float salesTax = RetrieveLocalSalesTax();
foreach( Widget w in widgets ) {
widgetPrices.Add( CalculateWidgetPrice( w, salesTax ) );
}
小部件或计算量大的CalculateWidgetPrice(Widget)方法,此循环将无法充分利用任何可用的内核。要在不同内核上进行价格计算,将要求程序员显式创建和管理线程,传递工作并一起收集结果。 :
var widgetPrices = widgets.Select( w=> CalculateWidgetPrice( w, salesTax ) );
foreach循环已移至Select方法中,隐藏了其实现详细信息。程序员剩下的就是告诉选择要应用于每个元素的功能。这将允许Select实现以并行方式运行计算,无需程序员参与即可处理所有同步和线程管理方面的问题。这就是不可变性的地方。Select的实现不知道所提供的函数(上面的CalculateWidgets)没有副作用。该功能可能会在Select及其同步范围之外更改程序的状态,从而破坏所有功能。例如,在这种情况下,salesTax的值可能会错误地更改。纯粹的函数式语言提供了不变性,因此Select(映射)函数可以确定没有状态发生变化。看起来像:
var widgetPrices = widgets.AsParallel().Select(w => CalculateWidgetPrice( w, salesTax) );
充分利用了系统的所有内核,而无需显式管理这些内核。
评论
我确实指出了对更多并行和并发处理的渴望,我在第四段中链接到的“ Aerlang的历史” ACM文章中对此进行了讨论。但这是一个很好的观点,我应该对此进行更多扩展。 +1,因为现在我不必; P
– yannis
2012年11月3日10:49
您说得对,我的视线不够仔细。我编辑了我的话。
–本
2012年11月3日10:52
哦,您并不是真的必须这样做,我不是在抱怨;)
– yannis
2012年11月3日,10:53
您在上面描述的内容均不需要lambda。使用命名函数可以轻松实现相同的功能。在这里,您只是在记录原因和可感知的影响,而无需说明相关性。 IMO的最后一行是问题所在。但您没有回答。为什么它简化了并发编程。
–马丁·约克
2012年11月3日13:38
@Ben:请注意,您的示例涉及不需要使用匿名函数的高阶函数。您的答案包含有趣的想法(针对另一个问题),但现在不在您的讨论范围内。
–乔治
2012年3月3日14:21
#6 楼
我同意这里的许多答案,但是有趣的是,当我了解了lambda并跳入它们时,并不是出于其他人提到的任何原因。函数只是提高了代码的可读性。在lambda之前,当您调用接受函数指针(或函数或委托)的方法时,必须在其他位置定义该函数的主体,因此,当您具有“ foreach”构造时,您的读者将不得不跳到另一个地方。代码的一部分,以查看您打算对每个元素确切执行什么操作。您正在阅读代码,功能保持不变,但是读者不必来回跳动,整个实现就在他的面前。许多功能编程技术和并行化可以在没有匿名功能的情况下实现;只需声明一个常规变量,并在需要时传递引用即可。但是使用lambda可以简化代码编写和阅读代码的实现。
评论
很好的解释(+1)。自1958年以来,Lisp程序员就意识到了这一切。
–乔治
2012年11月4日19:05
@ Giorgio:当然可以,但是Lisp程序员还必须购买带有增强的打开/关闭括号键的特殊键盘:)
– DXM
2012年11月4日在22:28
@DXM:不是键盘,它们获得了附加的输入设备,类似于用于打开和关闭括号的钢琴踏板;-)
–vartec
13年2月11日在13:55
@ DXM,vartec:最近正在做一些Scheme,我发现括号可以。某些C ++代码可能更含糊不清(我对C ++的经验比对Scheme的经验还多)。 :-)
–乔治
13年3月28日在12:25
#7 楼
在这里涉及到最近的历史时,我相信一个因素是在Java和.NET中增加了泛型。这自然会导致Func <,>和其他强类型的计算抽象(Task <>,Async <>等)。在.NET世界中,我们精确添加了这些功能以支持FP。这触发了一系列与函数式编程有关的语言工作,尤其是C#3.0,LINQ,Rx和F#。这种进展也影响了其他生态系统,并且在C#,F#和TypeScript中一直沿用至今。同样也有许多其他影响(当然是JS),而这些步骤又受许多其他因素的影响-但是将泛型添加到这些语言中,有助于打破90年代后期在软件世界大部分地区的严格的OO正统观念,并为FP。Don Syme
评论
欢迎!我将2005用于F#,因为那是F#的Wikipedia文章中报告的第一个稳定版本的年份。您想要我将其更改为2003吗?
– yannis
2012年11月4日17:16
#8 楼
这并不是一个认真的答案,但这个问题使我想起了James Iry的一个幽默幽默的帖子-简短,不完整和最错误的编程语言历史,其中包括以下短语: >“ Lambda被降级为相对模糊,直到Java通过不让它们流行起来为止。”评论
黄金短语:)
–开心果
13年2月11日在13:25
#9 楼
从我看来,大多数答案都集中在解释为什么函数式编程通常会卷土重来并使其成为主流。我觉得这并没有真正回答关于匿名函数的问题,以及为什么它们突然变得如此流行。真正流行的是封闭。因为在大多数情况下,闭包是通过变量传递的一次性函数,所以显然对它们使用匿名函数语法是有意义的。实际上,在某些语言中,这是创建闭包的唯一方法。
为什么瓶盖越来越流行?因为它们在事件驱动的编程中很有用,所以在创建回调函数时。当前,这是编写JavaScript客户端代码的方式(实际上,这是编写任何GUI代码的方式)。当前,它也是编写高效后端代码和系统代码的方式,因为以事件驱动范例编写的代码通常是异步且无阻塞的。对于后端,这已成为解决C10K问题的流行方法。
评论
感谢您强调此问题与函数式编程无关(+1),因为(1)非参数语言(例如Smalltalk)也使用作为参数传递的代码块的思想,以及(2)改变状态从闭包的词法上下文中捕获(在许多lambda实现中可能)绝对是不起作用的。是的,有了闭包,匿名闭包的步骤很短。有趣的是,闭包也早已众所周知,并且自八十年代以来就使用了事件驱动的编程(据我所知)。
–乔治
13年2月11日在13:18
但是也许直到最近几年才清楚可以比以前想象的更多地使用闭包。
–乔治
13年2月11日在13:27
@Giorgio:是的,当前使用的大多数概念已经存在了很长时间。但是它们还没有被使用,现在已经被使用了。
–vartec
13年2月11日在13:36
#10 楼
我认为原因是并发和分布式编程的普及率越来越高,面向对象编程的核心(用对象封装变化的状态)不再适用。在分布式系统的情况下,因为没有共享状态(并且该概念的软件抽象是泄漏的),在并发系统的情况下,因为正确同步对共享状态的访问已被证明麻烦且容易出错。就是说,面向对象编程的主要好处之一不再适用于许多程序,这使得面向对象范例的帮助远不如以前。相反,功能范式不使用可变状态。因此,我们从功能范例和模式中获得的任何经验都可以立即转移到并发和分布式计算中。现在,该行业无需重新发明轮子,而是借用了这些模式和语言功能来满足其需求。
评论
某些主流语言(例如C ++ 11)中的匿名函数确实允许可变状态(它们甚至可以从定义的环境中捕获变量,并在执行过程中对其进行更改)。因此,我认为谈论一般的功能范式,尤其是关于不变性,这超出了所要提出的问题的范围。
–乔治
2012年11月3日14:37
刚阅读Java 8的一些功能说明时,lambda项目的主要目标之一就是支持并发。这立即将我们带入了所有这些出色的Javabean都会遇到的可变性clusterbomb。一旦Java获得了lambda(假设它确实在版本8的最终版本中确实存在),那么他们就需要解决默认的不可变问题(以某种方式破坏语言,以Lisp的方式考虑-副作用免费功能)在COBOL中-重击DATA DIVISION / COPYBOOK)
– Roboprog
2013年12月3日,2:49
说得好。摆脱可变状态使并发变得更加容易,而诸如cascalog和spark之类的技术可以轻松地在计算机集群之间分配功能性编程。有关如何以及为什么的更多详细信息,请参见glennengstrand.info/analytics/distributed/functional/…。
– Glenn
2014年12月14日,0:56
#11 楼
如果可以增加我的0.02欧元,尽管我会同意JavaScript引入该概念的重要性,但我认为除了并发编程之外,我还应将责任归咎于异步编程的当前方式。当进行异步调用(网页需要)时,简单的匿名功能非常有用,以至于每个Web程序员(即每个程序员)都必须非常熟悉该概念。#12 楼
类似于匿名函数/ lambdas的另一个真正古老的例子是Algol 60中的按名称调用。但是请注意,按名称调用比传递真实函数更接近于将宏作为参数传递,并且更脆弱/更难结果明白了。#13 楼
据我所知,这里是祖先。2005:Javascript最近将带lambda的高阶编程重新带入了主流。特别是像underscore.js和jquery这样的库。这些库中的第一个是prototype.js,它比jquery早一年。原型基于Ruby的Enumerable模块,这使我们进入了……
1996:Ruby的Enumerable模块显然很受Smalltalk的收集框架的启发。正如Matz在许多采访中提到的那样,这导致我们……1980年:Smalltalk使用了大量的高阶编程,并提供了一个集合API,该API大量使用了高阶编程(例如,GNU Smalltalk的Iterable类)。在惯用的Smalltalk代码中,您不会找到任何for循环,而只会找到高阶枚举。不幸的是,当Java在1998年将Smalltalk的集合框架移植到Java时,省略了高阶枚举。这就是在未来十年内逐步淘汰主流编程的方式! Smalltalk有很多祖先,但是与OP问题相关的是LISP,这使我们走向…
1958:LISP显然以高阶编程为核心。
评论
当然,Amiss是整个ML祖先。 ML,SML,OCaml,Haskell,F#。那一定很重要。
–穆罕默德·阿尔卡鲁里(Muhammad Alkarouri)
13年2月11日在13:40
#14 楼
匿名函数很好,因为很难命名,而且如果只在一个地方使用一个函数,则不需要名称。Lambda函数直到最近才成为主流,因为直到最近,大多数语言不支持闭包。
我建议Javascript推动了这一主流。这是一种通用语言,无法表达并行性,匿名函数简化了回调模型的使用。此外,诸如Ruby和Haskell之类的流行语言也做出了贡献。
评论
“ Lambda函数直到最近才成为主流,因为直到最近,大多数语言还不支持闭包。”:这种推理对我来说有点循环:成为主流意味着大多数语言都支持闭包。人们可能会立即问到“是什么导致了现代编程语言中闭包的流行”。
–乔治
2012年11月6日14:45
我知道Python并不是lambda的最佳实现。但就受欢迎程度而言,它的贡献可能超过Haskell。
–穆罕默德·阿尔卡鲁里(Muhammad Alkarouri)
13年2月11日在13:42
评论
15到20年前,人们对OO提出了同样的问题...这不是一个新概念,但它的普及程度很高。@MattDavey大多数人肯定会不同意,但随后我不得不提醒他们,“大多数Smalltalk开发人员”并不是很多人; P
我认为更有趣的问题是什么导致了他们的死亡!毕竟,有一段时间,大多数现代语言都具有lambda,然后像Java和C ++这样的语言开始流行。 (尽管我不会完全将Java称为“现代”语言。Java中最现代的概念是泛型,它可以追溯到60年代末/ 70年代初。甚至Java提供的功能组合,指针安全性,内存安全性,类型安全性,GC,静态类型的OO,泛型都在1985年在Eiffel中存在……更好的是,恕我直言。)
甚至在Java 1.0推出之前,尽管它仍处于早期设计阶段,但几乎所有人都指出Java需要lambda。使用Java的一些设计师包括Guy Steele(Lisp的支持者,Scheme的共同设计师,Common Lisp的共同作者,Fortress的设计师),James Gosling(撰写了第一个PC的Emacs Lisp解释器),Gilad Bracha(Smalltalk)支持者,Ammorphic Smalltalk联合设计师,Newspeak设计师,Phil Wadler(Haskell联合设计师),Martin Odersky(Scala设计师)。 Java最终没有lambdas的结果真的超出了我。
“一点点”通常意味着50%的功能,50%的噪音。