如果我们可以使用Python进行函数式编程,是否需要特定的函数式编程语言?[关闭]


22

使用生成器和lambda,我们可以使用Python进行函数式编程。您也可以使用Ruby实现相同的功能。

所以问题是:为什么我们需要特定的功能编程语言,例如Erlang,Haskell和Scheme?这些特定的功能编程语言提供了什么不同?为什么我们不能只使用Python进行函数式编程?


30
所有这些都是围绕之前就被创造蟒蛇
马哈茂德·霍山

51
福特Pinto是汽车。为什么我们需要像法拉利这样的特定快车?
马丁·约克

11
使用类和模板,我们可以在C ++中执行任何OO操作。为什么曾经创建Java和Python?他们增加了什么?
9000

19
所有编程语言(不含某些纯学术研究语言)都与Turing等效,因此您可以使用语言A进行操作,也可以使用任何其他语言进行处理。因此,按照这种思路,我们只需要一种图灵完整的语言-就像sendmail.cf之类的东西就可以了) okmij.org/ftp/Computation/#sendmail-Turing
Maglob 2011年

17
如果您知道这些语言中的任何一种,就不会声称Python可以很好地完成函数式编程。没有。它可以很好地合并一部分FP类的东西,但没有任何更好的选择。

Answers:


20

我很欣赏这个问题,因为我个人非常喜欢Python和函数式编程。我有很长的Python背景,最近我开始学习Haskell,因此,根据我的个人经验,从功能的角度来看这些语言之间的区别,这里有几点。

纯度

即使您根本不关心函数的纯度(即没有副作用),它对于读取代码的难易程度和原因也有实际的影响。即使您在自己的Python函数中保持纯净,也要使编译器强制执行纯净,并且最重要的是,具有基于纯净和不可变数据结构构建的标准库也有很大的不同。

性能

您可能会或可能不会在乎性能,这取决于您的应用程序域是什么,但是与Python和其他动态语言相比,静态类型和有保证的纯度使编译器可以使用更多的功能(尽管我不得不承认PyPy的确很棒入侵,例如LuaJIT奇迹般地临近。

尾叫优化

与性能相关,但略有不同。即使您不太在意运行时性能,也没有进行尾部调用优化(尤其是尾部递归)会限制您在Python中实现算法的方式而不会达到堆栈限制。

句法

这就是为什么我开始看“真正的”功能语言而不是仅仅使用具有功能样式的Python的最大原因。尽管我认为Python通常具有非常有表现力的语法,但是它具有一些特定于函数编码的弱点。例如:

  • lambda函数的语法非常冗长,并且所包含的内容受到限制
  • 没有语法糖功能组成,即f = g . hf = lambda *arg: g(h(*arg))
  • 没有用于部分应用的语法糖,即f = map gvs.f = functools.partial(map, g)
  • 没有在高阶函数中使用infix运算符的语法糖,即sum = reduce (+) lstvs.sum = reduce(operator.add, lst)
  • 没有模式匹配或函数参数的保护,这使得使用易于理解的语法很容易表达递归结束条件和某些边界情况。
  • 括号对于函数调用从来都不是可选的,并且嵌套调用没有语法糖。我想这是一个问题,但是特别是在功能代码中,我发现链接函数调用很常见,而且一旦您熟悉该符号,我就会发现它y = func1 $ func2 $ func3 x比容易阅读y = func1(func2(func3(x)))

28

这些是最重要的区别:

哈斯克尔

  • 懒惰评估
  • 编译为机器码
  • 静态类型化确保函数纯净
  • 类型推断

Haskell和Erlang

  • 模式匹配

Erlang

  • 并发,轻量级流程的参与者模型

方案

  • 巨集

所有语言

  • 真正的闭包(ruby有闭包,是否可以讨论python确实存在争议,请参阅注释)
  • 适用于函数式编程样式(不可变集合,地图,过滤器,折叠等)的标准库
  • 尾递归(也可以在某些非功能性语言中找到)

此外,您还应该看看ML系列中的语言,例如SML,Ocaml和F#和Scala,它们以一种新的方式融合了OO和功能编程。所有这些语言都有独特有趣的功能。


3
+1好帖子。您可以在Haskell上添加类型推断,并在Erlang上添加轻量级进程。
乔纳斯(Jonas)

1
Python确实具有map,filter和fold(reduce)。关于“实数闭包”:如果将实数闭包定义为可以包含除单个表达式之外的其他内容的闭包,Haskell也没有实数闭包(但是Haskell当然也有一些不是表达式的东西...) 。但是,关于不变数据结构的观点是一个很好的观点,因此+1。另外:尾递归(通常便宜些的递归)。
sepp2k 2011年

2
+1为“静态类型化确保功能纯净”。具有区分纯函数和非纯函数的类型系统非常酷。(C ++的const doe snot计数。:)
Macke'2

1
@btilly:如果您不能分配给范围内的变量,我不会认为它是闭包。否则,我们不得不说Java也有闭包,因为您可以在其中使用相同的技巧。

3
在闭包中,我可以像平常一样访问变量。对于Haskell和Ruby的闭包,这是正确的,但对于Python或Java的不当替代品,则不是这样。也许其他人可以启发我们有关erlang的知识,但我不太了解。

19

很难确切地定义“功能语言”是什么-在您列出的语言中,只有Haskell是纯粹的功能(所有其他语言都采用某种混合方法)。但是,某些语言功能对函数式编程非常有帮助,而Ruby和Python不足以成为FP的良好环境。这是我的个人清单,按重要性排序:

  1. 一流的函数闭包(Ruby,Python和您列出的所有其他都具有此功能)。
  2. 保证尾部呼叫优化(Erlang,Haskell,Scala和Scheme具有此功能,但Python,Ruby或Clojure(尚未))。
  3. 支持不变性语言和标准库中的(这是您列出的所有“功能性语言”都具有的一种大语言(Scheme除外),而Ruby和Python没有)。
  4. 引用透明(或纯)函数的语言级别支持(据我所知,目前只有Haskell拥有此功能)。

对(1)的需求应该很明显-如果没有一流的函数,高阶函数将非常困难。当人们谈论Ruby和Python是FP的好语言时,他们通常在谈论这一点。但是,此特定功能是必需的,但不足以使语言适合FP。

自从Scheme发明以来,(2)一直是FP的传统必需品。没有TCO,就不可能进行深度递归编程,深度递归是FP的基石之一,因为您会遇到堆栈溢出的情况。Clojure(由于JVM的限制)是唯一不具备此功能的(按流行的定义)语言(由于JVM的限制),但是Clojure具有多种可模拟TCO的技巧。(仅供参考,Ruby TCO是特定于实现的,但Python 特定地不支持它。)必须保证TCO的原因是,如果它是特定于实现的,则深度递归函数会因某些实现而中断,因此您不能真正做到这一点。完全使用它们。

(3)是现代功能语言(尤其是Haskell,Erlang,Clojure和Scala)具有Ruby和Python没有的另一大优点。无需赘述,保证不变性消除了所有类型的错误,尤其是在并发情况下,并且允许诸如持久数据结构之类的整洁事物。没有语言级别的支持,要利用这些好处是非常困难的。

对我而言,(4)是关于纯功能语言(与混合语言相对)最有趣的事情。考虑以下极其简单的Ruby函数:

def add(a, b)
  a + b
end

这看起来像是一个纯函数,但是由于操作员重载,它可能会改变参数或导致副作用,例如打印到控制台。不太可能有人会给+操作员带来过多的副作用,但是这种语言并不能保证。(这同样适用于Python,尽管可能不适用于此特定示例。)

另一方面,在纯功能语言中,存在语言级的保证,即功能是参照透明的。这具有许多优点:可以轻松记住纯功能;可以轻松测试它们,而无需依赖任何全局状态;函数中的值可以延迟或并行地求值,而不必担心并发问题。Haskell充分利用了这一点,但是我对其他功能语言的了解还不够,他们是否知道。

综上所述,几乎所有语言(甚至Java)都可以使用FP技术。例如,谷歌的MapReduce受到功能思想的启发,但是据我所知,他们的大型项目没有使用任何“功能”语言(我认为他们大多使用C ++,Java和Python)。


2
+1为详尽的解释-即使是作为FP局外人的我也明白这一点。谢谢!:-)
彼得Török

迄今为止,这个问题的最有价值的答案。基于事实和大量信息。做得好。
维尔贝尔

Scala具有尾部递归,并且与Scheme一样,如果递归调用位于尾部位置,则它会自动完成(与Clojure不同,后者必须明确请求它)。甚至还有一个注释,因此您可以检查编译器是否会生成尾部递归代码。它没有的是Scheme的更通用的TCO。我认为您知道这一点,但是由于您对大多数其他事情都做了很多详细介绍,所以这似乎是一个奇怪的解雇/遗漏。
itsbruce

@itsbruce这篇文章很老,IIRC Scala当时没有(或者我可能是错了;)。更新。
shosti

从一开始我就没有使用过Scala,但是在我感兴趣的时候,它在2008年确实进行了尾递归操作;-)我将向这个主题提问的人转给这个特定的SO问题,因为它有一些不错的答案,我只是注意到这种奇怪之处,因此评论其完整性。
itsbruce

11

您提到的语言非常不同。

虽然Python和Ruby是动态类型的语言,但是Haskell是静态类型的。Erlang是一种并发语言,使用Actor模型,与您提到的所有其他语言有很大不同。

Python和Ruby具有许多命令式构造,而在像Haskell这样更纯净的功能语言中,一切都返回某种东西,换句话说,一切都是函数。


@kRON:好吧,类型系统是一种语言的重要属性,他问“这些特定的函数式编程语言提供了什么不同?”。当然,您可以将Actor模型与其他语言一起使用,但是Erlang内置了该语言。Erlang使用轻量级进程,并且具有用于分布式编程的内置语言构造-因此是“并发”语言。
乔纳斯(Jonas)

8

像往常一样晚参加聚会,但无论如何都要说些什么。

功能编程语言不是允许功能编程的语言。如果按照这个定义去做,那么几乎任何地方的任何语言都是一种功能性编程语言。(顺便说一句,同样适用于OOP。如果愿意,可以用C用OOP风格编写。因此,根据您的逻辑,C是一种OOP语言。)

使得功能性编程语言脱颖而出的并不是它允许您进行编程的方式,而是它使您可以轻松进行编程的方式。那是关键。

因此,Python具有lambda(它们是令人难以置信的类似于闭包的事务),并为您提供了一些库函数,这些库函数将在功能库中看到,例如“ map”和“ fold”。但是,这还不足以使其成为一种功能性的编程语言,因为很难以一致的功能风格一致地进行编程(而且该语言肯定不会强制采用这种风格!)。Python的核心是与状态和状态操纵操作有关的命令式语言,这与功能语言的表达式和表达式评估语义完全不同。

那么,当Python(或Ruby(或插入您选择的语言))可以“执行函数式编程”时,为什么我们有函数式编程语言呢?因为Python等无法进行适当的函数式编程。这就是为什么。



4

这个问题可以应用于无数种语言和范例。

  • 由于每个人都使用C ++,为什么我们需要其他通用语言?
  • 由于Java是一种很棒的OO语言,为什么还存在其他OO语言?
  • 由于perl是一种了不起的脚本语言,为什么我们需要python?
  • 亚塔亚塔亚塔

大多数(如果不是全部)语言都是出于特定原因而存在的。它们之所以存在,是因为有人需要没有一种当前的语言能填补或填补不好的语言。(当然,这并不适用于每一种语言,但我觉得它适用于大多数知名的语言。)例如,蟒蛇最初是与阿米巴OS [接口12 ]和Erlang是为了帮助在电话应用开发中[ 3 ]。因此,一个问题的答案是“为什么我们需要另一种功能语言?” 可能很简单,因为[插入一个知道如何设计语言的人的名字]不喜欢python的方式。

这几乎可以概括我认为的答案。尽管您可以使用python做任何可以使用功能语言完成的事情,但您真的要这么做吗?您可以使用C进行的所有操作,都可以使用汇编进行的操作,但是您想这样做吗?不同的语言总是总是最擅长做不同的事情,这就是应该的方式。


2

函数式编程既涉及设计范例,也涉及特定的语言功能。或者,换句话说,lambda和map函数不是函数式编程语言。Python和Ruby具有受功能编程语言启发的某些功能,但是您通常仍然以非常必要的方式编写代码。(有点像C:您可以用C编写类似OO的代码,但没有人认真地认为C是一种OO语言。)

请看:函数式编程不仅仅与lambda或map或更高阶函数有关。这与设计有关。用“真正的”函数式编程语言编写的程序通过函数的组合解决了问题。尽管用Ruby或Python编写的程序可能使用类似于FP的功能,但它们通常读起来并不像一组组合函数。


0

您提到的每种功能语言都非常适合特定的领域,每种形式所鼓励的惯用模式使它们非常适合某些任务,除非您建立了庞大的帮助程序模块库,否则这些任务在Python中是无法完成的。此类利基优势中最明显的就是Erlang的并发模型。其他的也有类似的优势。


0

lambda演算,LISP和Scheme中可用的每个概念都可以在Python中使用,因此,是的,您可以在其中进行函数式编程。是否方便,取决于上下文和品味。

您可以轻松地用Python(Ruby,Scala)编写LISP(或其他功能语言)解释器(这意味着什么?)。您可以使用纯功能为Python编写解释器,但是这需要大量工作。如今,即使是“功能性”语言也已成为多种范例。

这本古老而美丽的书提供了(尽管不是全部)大多数有关函数式编程本质的答案。


@Arkaaito但是,您是否同意lambda演算,LISP和Scheme中可用的每个概念都可以在Python中使用?
Mark C

您确定每个 lisp概念都可以在Python中使用吗?宏,代码就是数据吗?
SK-logic

@ SK-logic宏不在lambda-calculus中,但是是的,它们在Python中可用,尽管不是该名称。Python提供了一个eval()功能,即数据需要代码,但这是更进一步的:它使您可以像LISP中一样修改大多数运行时环境。
2011年

@Apalala,动态语言eval()是运行时元编程。有时它很有用,但是却非常昂贵。Lisp宏是不同的,它是一个编译时的元编程。在Python中没有可用的任何可用形式。
SK-logic

@ SK-logic这种宏打破了代码就是数据的前提,因为程序无法更改宏(甚至不知道它们的存在)。在Python中,如果需要,程序可以在运行时访问其自己的解析树。宏(静态预处理)根本不起作用。
2011年

-5

因为Python可以计划在非功能性的风格,这是不够好为FP纯粹。但是,更多实用的编码人员可以享受功能样式的好处,而不必执迷于此:

“功能程序员听起来像中世纪的和尚,否认自己过着快乐的生活,希望这会使他有德。对于那些对物质利益更感兴趣的人来说,这些“优势”并不是很令人信服。函数式程序员辩称存在巨大的物质利益…[但是]这显然是荒谬的。如果省略赋值语句带来如此巨大的收益,那么FORTRAN程序员将已经这样做了二十年。通过忽略功能(无论功能有多糟糕)来使语言更强大,这在逻辑上是不可能的。”

-John Hughes,为什么函数式编程很重要


6
我同意。没有任何语言goto都是可怕的。
Anon。

2
@Anon:还是它的大同胞call-with-current-continuation
克里斯·杰斯特·杨

4
梅森,您引用的论文正好相反​​。您的引文只是一篇讲述几个不同故事的论文的几个段落的一部分。实际上,如果您同时引用了这两段,则它们显然会显示另一种含义。
Marco Mustapic 2011年

2
@梅森:是的,作者声称模块化和惰性评估是真正的好处。但请注意,您的原始报价是如何脱离上下文的-您似乎暗示作者对FP提出了主张,而实际上您引用了一篇论文中的一段,得出的结论是FP很好,而并非出于“少即是多”。论文的标题很清楚地表明了作者的意图:“为什么函数式编程很重要” ;-)它当然不支持您的观点,即纯粹的FP languanges是“令人讨厌的事情”。
Andres F.

6
@Mason Wheeler:混合语言比Python早很多。例如,Lisp的历史可以追溯到1959年,实际上是一种多范式语言。它完全支持函数方法,过程方法和面向对象的编程方法。使用正确的宏程序包,您也可以非常轻松地进行逻辑编程。Scheme也早于Python(和本文)。它可以追溯到1975年。也许您有时应该浏览一下编程语言的时间表
只是我的正确观点,
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.