Ruby是一种函数式编程语言吗?

93

维基百科称Ruby是一种函数式语言,但我并不信服。你认为呢?


4
可能因为你的问题非常简短,不过就我个人而言,我完全没有任何问题! - ljs
已经有很好的答案了,为了补充它们,这里提供一些关于FP和Ruby的内容:http://code.google.com/p/tokland/wiki/RubyFunctionalProgramming http://www.slideshare.net/tokland/functional-programming-with-ruby-9975242 - tokland
1
如果有人对这个话题感兴趣,请观看此视频,您将学习到如何使用Ruby进行函数式编程,函数式编程的根源是什么,为什么即使Ruby能够进行函数式编程,它也不是一种函数式语言:https://www.youtube.com/watch?v=5ZjwEPupybw - maddin2code
12个回答

62

语言是否是函数式语言并不重要。函数式编程是一个论题,由Philip Wadler(函数式编程的本质)和John Hughes(为什么函数式编程很重要)最好地解释。

一个有意义的问题是,“Ruby对实现函数式编程的论题有多容易?” 答案是“非常困难”。

我最近就这个问题发表了演讲。这是幻灯片。


3
你提供的幻灯片没有提到为什么Ruby“很难实现FP论文”的原因。为什么C#比Java更适合(匿名函数更容易?)?这是因为Ruby可以有全局变量吗? - kizzx2
7
不,这些幻灯片没有深入探讨这个话题,因为这是一个相当广泛的主题。举例来说,Ruby强制实施了一个评估模型(按值调用),确保程序的不可组合性。这样做的影响很容易被低估。此外,Ruby还坚持认为程序是一系列效果的序列。也就是说,Ruby会努力使使用任何其他计算模型变得困难或棘手。希望这个简短的评论能有所帮助。 - Tony Morris
2
+1 指出将语言分类为函数式存在歧义。我曾经写过函数式 C! - Eli
1
为什么C#比Ruby更易于处理? - dan_l
1
这实际上是一个只有链接的答案,因为它将解释的关键部分(事实上整个解释)外包到外部链接。现在该链接已经失效,这个答案已经变得毫无用处。 - ivan_pozdeev
显示剩余3条评论

37

Ruby支持更高级别的函数(参见Array#map,inject和select),但它仍然是一种命令式的面向对象语言。

函数式语言的一个关键特征是避免可变状态。函数式语言没有像Ruby、C、Java或其他命令式语言中那样的变量概念。

函数式语言的另一个关键特征是将程序定义为“什么”,而不是“如何”。在面向对象的语言中编程时,我们编写类和方法来隐藏实现(“如何”)对于“什么”(类/方法名称)的细节,但最终这些方法仍然是使用语句序列编写的。在函数式语言中,您不需要指定执行顺序,即使在最低级别也是如此。


3
我同意你的大部分说法,但我不同意“函数式编程语言中没有像Java等语言中那样的变量概念”的观点。在Haskell中,你可以在纯函数中使用变量,甚至可以将一个函数赋值给一个变量。最大的区别是,一旦一个变量被赋值后就不能再修改它。 - HHC
6
根据定义,"变量"是一个可以改变的值。你所说的是"数值"。 - Scala Newb
事实上,Haskell的“不可修改变量”只是没有参数(定义)的常量函数。 - raindev

30

我认为在Ruby中可以使用函数式编程风格。

要能够以函数式编程的方式编程,最关键的一点是语言是否支持高阶函数...而Ruby确实支持此功能。

话虽如此,在Ruby中也很容易以非函数式编程风格进行编程。函数式编程的另一个关键方面是不具有状态,并且拥有真正的数学函数,这些函数始终对于给定的一组输入返回相同的值。这在Ruby中也可以做到,但它并不能像Haskell等更严格的函数式语言那样强制执行。

因此,是的,Ruby支持函数式编程风格,但也允许您使用非函数式编程风格进行编程。


4
根据这个标准,你会说Smalltalk是一个函数式编程语言吗?因为它有块(blocks)? - OscarRyz
回答不错,但有一个小问题——高阶函数并不是函数式风格的必要条件。例如,在Java中(它没有一级/高阶函数),您可以通过定义函数对象并将它们组合来实现函数式风格,从而获得与高阶函数相同的效果。 - mikera
2
只是想说明一下,@peter问过“Ruby是一种函数式语言吗?”直截了当的回答是不是。Ruby是一种面向对象的语言,具有一些函数式特性。 - Elias Perez

16

我认为,支持或具有使用函数式 风格 编程的能力并不代表这是一个函数式语言。

如果我想要折磨我的同事和自己几个 星期,我甚至可以用函数式风格编写Java代码。

拥有函数式语言不仅仅在于你 可以 做什么,例如高阶函数、一级函数和柯里化。它也关乎于你 不能 做什么,例如在纯函数中引入副作用。

这一点很重要,因为它是功能性程序易于理解的一个重要原因。当代码易于理解时,错误变得更加显而易见,并且浮现出来,这样就可以进行修复,从而产生更少的错误代码。

Ruby 本质上是面向对象的,因此即使它对函数式风格有相当良好的支持,它本身也不是一个函数式语言。

这是我非科学的观点。

编辑: 回顾并考虑到我迄今收到的精美评论,我认为面向对象与函数式比较有点类似苹果和橙子之间的区别。

真正的区别在于是否以命令式方式执行。函数式语言将表达式作为其主要的语言构造,执行顺序通常是未定义的或定义为惰性。严格执行是可能的,但只有在需要时才使用。在命令式语言中,严格执行是默认值,虽然惰性执行是可能的,但在许多边缘情况下,这通常很糟糕并且可能具有不可预测的结果。

现在,这是 我非科学的观点。


我认为你可以更好地说明为什么称Ruby为函数式编程语言比Java更合适...不,Ruby并不是严格的函数式编程语言,但在其中使用函数式编程风格相当容易...而一个非函数式编程风格的同事也可以轻松地将其改回非函数式编程。 - Mike Stone
1
是的,迈克,如果你想以函数式编程风格编写代码,那么Ruby比Java要好得多。我只是使用Java来夸大和强调这一点。 - Chris Vest
那么既然D具有纯函数,你会称D为一种函数式语言吗?http://www.digitalmars.com/d/2.0/function.html#pure-functions - Peter Burns
3
许多人认为Lisp和Scheme是函数式语言,主要是由于匿名函数的普遍使用。然而,它们缺乏保证纯函数的特性。将术语限制在支持纯函数的语言范围内似乎过于严格。 - skymt

14
为了真正实现“函数式编程”,Ruby需要满足以下要求: 不可变的值:一旦“变量”被设置,就不能更改。在Ruby中,这意味着你必须将变量视为常量。这在语言中没有得到完全支持,因此您需要手动冻结每个变量。 无副作用:给定一个值时,函数必须始终返回相同的结果。这与具有不可变值密切相关;函数永远不能接受一个值并更改它,因为这将导致与返回结果无关的副作用。 高阶函数:这些是允许函数作为参数或使用函数作为返回值的函数。这可能是任何功能性语言中最重要的特征之一。 柯里化:由高阶函数启用,柯里化是将接受多个参数的函数转换为接受一个参数的函数。这与部分函数应用程序紧密相关,后者将多参数函数转换为比原来少的参数函数。 递归:通过从函数内部调用自身进行循环。当您无法访问可变数据时,递归用于构建和链接数据结构。这是因为循环不是函数概念,因为它需要传递变量以存储循环在给定时间的状态。 延迟评估:将值的处理推迟到实际需要它的时刻。例如,如果您有一些启用了延迟评估的生成斐波那契数列的代码,则在另一个函数(如puts)要求结果中的一个值时才会实际处理和计算。 建议(仅供参考):mode指令定义为声明具有函数式编程范式的文件,例如:

mode 'functional'


2
不用谢。我想邀请你了解一下函数式编程语言。Lisp是所有函数式语言的鼻祖,还有ML(CAML)和Erlang / Elixir。它真的可以改变你对事物的看法。虽然我并不是专家,但我一直是计算机科学的学生,喜欢阅读和学习新东西。 - Elias Perez
回答很有条理。希望能更深入地探讨Ruby对这些功能的支持程度。我相信Ruby支持高阶函数、柯里化和递归,如果我错了,请纠正我。 - Michael Dorst

9

Ruby是一种支持函数式编程风格的多范式语言。


4

Ruby是一种面向对象的语言,可以支持其他范式(函数式、命令式等)。然而,由于Ruby中的所有东西都是对象,因此它主要是一种面向对象的语言。

例如:

"hello".reverse() = "olleh",每个字符串都是一个字符串对象实例,依此类推。

这里这里了解更多信息。


我从未真正理解“一切皆为对象”如何使Ruby更加面向对象。我确实同意Ruby主要是面向对象的,但“一切皆为对象”实际上只意味着没有“原始”类型,这对开发人员以面向对象的方式编写程序的能力几乎没有任何影响,因为原始类型的存在通常只意味着有四个或五个类型没有任何方法。 - Michael Dorst

4
这要取决于您对“函数式语言”的定义。个人认为,当它被用作绝对概念时,这个术语本身就有很大问题。成为“函数式语言”的方面不仅仅是语言特性,而且大多数取决于您的视角。例如,语言周围的文化在这方面非常重要。它是否鼓励函数式风格?可用库如何?它们是否鼓励您以函数式方式使用它们?
例如,大多数人会称Scheme为函数式语言。但是Common Lisp呢?除了多/单命名空间问题和保证尾调用消除之外(某些CL实现也支持,具体取决于编译器设置),并没有太多让Scheme作为一种语言更适合函数式编程的东西,但是,大多数Lisper不会将CL称为函数式语言。为什么?因为周围的文化非常依赖于CL的命令式特性(例如LOOP宏,大多数Schemer可能会不同意)。
另一方面,C程序员可能会认为CL是一种函数式语言。毕竟,在任何Lisp方言中编写的大多数代码都比您通常的C代码块更具函数式风格。同样,与Haskell相比,Scheme非常是一种命令式语言。因此,我认为永远不可能有确定的是/否答案。是否将一种语言称为函数式语言在很大程度上取决于您的观点。

Haskell不是一个纯函数式语言的哪个方面存在问题?或者说Miranda(一种较少人知道的函数式编程语言)呢?https://courses.cs.washington.edu/courses/cse505/99au/functional/miranda.html "Haskell是一种标准的纯函数式语言。" - barlop

2

严格来说,将一种语言描述为“函数式”并没有什么意义;大多数语言都能够进行函数式编程,甚至包括C++。

函数式风格实际上是命令式语言特性的一个子集,使用了一些语法糖和编译器优化,比如不可变性和尾递归展开。

后者可能是一个次要的、与实际语言无关的技术细节。例如,x64 C# 4.0编译器进行了尾递归优化,而x86编译器由于某些愚蠢的原因则没有进行优化。

通常可以通过一些方式规避语法糖,特别是如果该语言具有可编程的预编译器(如C的#define)。

或许更有意义的问题是,“语言X是否支持命令式编程?”例如,对于Lisp来说,答案是否定的。


2
递归在函数式编程中很常见。几乎所有的语言都支持递归,但如果没有尾调用优化(TCO),递归算法通常是低效的。
函数式编程语言能够优化尾递归,并且可以在恒定的空间内执行这样的代码。一些 Ruby 实现确实优化了尾递归,其他的则没有,但一般来说,Ruby 实现不需要执行 TCO。请参见《Ruby 是否执行尾调用优化?》
因此,如果你编写一些 Ruby 函数式风格的代码,并依赖于某个特定实现的 TCO,你的代码可能在另一个 Ruby 解释器中非常低效。我认为这就是为什么 Ruby 不是一种函数式语言(Python 也不是)。

TCO很有趣,因为它基本上改变了程序的行为。在某些情况下,这对程序来说是不可见的,但在其他情况下,例如异常回溯,它是可见的。因此,它并不总是适合进行优化。 - ioquatix

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接