函数式编程与OOP [关闭]


93

最近,我听到很多关于使用功能语言(例如Haskell)的讨论。函数式编程与面向对象的编程之间的优缺点有哪些?


27
一个不拒绝另一个。
mbq 2010年

1
@mbq我知道它们并不互相排斥,但我只是想尝试更好地了解这两种方法的区别。
GSto

好问题。我也一直在想这个。
JohnFx 2010年

功能编程和面向对象的编程相互正交。两种语言都可以使用。例如:Scala,F#,OCaml等。也许您是按照乔纳斯的建议说功能还是命令?
missingfaktor

4
真正的答案是-他们之间没有“对立”。在StackOverflow上检查这个问题
missingfaktor 2010年

Answers:


67

我要说的是,函数式编程命令式编程更重要

最大的区别是命令式编程与控制流有关,而功能性编程与数据流有关。换句话说,函数式编程仅使用表达式,而命令式编程则同时使用表达式语句

例如,在命令式编程中,处理状态时会使用变量和循环,而在函数式编程中,则通过参数传递来处理状态,从而避免了副作用和赋值。

用于计算列表总和的函数的命令式伪代码(总和保存在变量中):

int sumList(List<int> list) {
    int sum = 0;
    for(int n = 0; n < list.size(); n++) {
        sum = sum + list.get(n);
    }

    return sum;
}

相同功能的功能伪代码(总和作为参数传递):

fun sumList([], sum) = sum
 |  sumList(v::lst, sum) = sumList(lst, v+sum)

我推荐Simon Peyton-Jones的演示“ 用函数编程驯服效果”,以很好地介绍函数概念。


12
您应该提到,该函数版本是尾递归的,因此经过优化,可以避免堆栈溢出。(某些人可能会看到递归,并因此认为函数式编程是不好的)
替代

3
+1用于描述命令性与功能性最重要的方面:控制流与数据流。我要补充的一件事是功能范式和OO范式不是互斥的。您可以使用OO范例来建模对象(数据)如何交互,并使用功能范例来转换(操作)该对象。
Lie Ryan

1
有趣的是,您可以对作为控制的数据和作为数据的控制进行建模以进行混合。FP可以使用Arrows和一阶函数来传递控制流并像数据一样操纵它。OOP使用各种设计模式来使用对象来更改控制流。
CodexArcanum 2010年

我认为还应该指出,主要区别不是您编写相同的程序,而是使循环执行尾递归方法调用。它比那还大得多
萨拉

您的功能示例使用了参数模式匹配。这并不是函数式编程所独有的,类似的函数式程序可以使用monad甚至是类似命令式的构造,而不必将每个迭代算法都公式化为递归算法。

16

函数式编程基于声明性模型,并且起源于lambda演算。它提供了许多很棒的概念,可以从诸如C ++和C#的命令式语言中借用。

一些示例包括参照透明性,lambda函数,一流函数,惰性和渴望评估以及不变性。

如果没有别的,学习函数式编程对于其中包含的概念很有用。它将改变您进行编程和思考编程的方式。而且我猜想将来函数式编程将和面向对象的编程一样重要。

首先,您可以选择使用诸如Haskell之类的纯函数式语言,也可以使用诸如F#之类的混合语言。

大多数优秀的大学都将涵盖功能编程,如果您要上学,我强烈建议您参加那门课程。


函数式编程与面向对象的编程之间的优缺点有哪些?

面向对象的编程很好,因为它允许您将复杂的问题建模为层次结构,从而可以简化问题。但是,当您开始考虑使用可变对象时进行多线程编程时,这将变得非常困难。在这种情况下,您需要大量使用同步对象,而完善大型应用程序几乎是不可能的。

那就是函数式编程的源头。由于不可变性之类的功能,函数式编程确实简化了多线程程序。当您知道给定输入X到函数将始终输出Y时,几乎可以轻松地并行化某些内容。此外,您知道变量(或函数编程中的值)不能在另一个线程的中间使用中更改。


2
需要明确的是,Scheme绝不是一种纯粹的功能语言。
乔纳森·斯特林

5
您的倒数第二段完全是bs。OO在多线程中没有任何问题,而可变性却没有。您似乎将命令式程序设计与面向对象程序设计混淆了。是这样吗
missingfaktor

5
@missingfaktor:不,我不会混淆这些概念。对象通常具有访问器,修饰符,数据成员和成员函数。是的,并非所有对象都需要具有修饰符,您可以将它们实现为不可变的。但是,如果您看任何任意的OO程序,则几乎可以肯定会有几个带有修饰符的对象,但它们仍被多线程使用。即在OOP范式中,几乎所有东西都是不可变的是很少见的。
Brian R. Bondy 2010年


另外这里检查弗兰克Shearar的回答是:programmers.stackexchange.com/questions/12423/...
missingfaktor

8

(此答案是从StackOverflow上的已解决问题答案改编而成的。)

函数式编程和面向对象的编程之间的最大区别之一是,每个编程人员都擅长于不同类型的软件演进:

  • 当您对事物进行固定的操作时,面向对象的语言是很好的选择,并且随着代码的发展,您主要添加新的事物。这可以通过添加实现现有方法的新类来完成,而现有类则不理会。

  • 当您拥有固定的事物集时,功能语言是很好的选择 ,并且随着代码的发展,您主要在现有事物上添加新的操作。这可以通过添加使用现有数据类型进行计算的新函数来完成,而现有函数则不予考虑。

当进化走错路时,您就会遇到问题:

  • 向面向对象的程序添加新操作可能需要编辑许多类定义以添加新方法。

  • 向功能程序中添加新事物可能需要编辑许多功能定义以添加新案例。

这个问题已经众所周知很多年了。1998年,菲尔·沃德勒Phil Wadler)将其称为“表达问题”。尽管一些研究人员认为可以使用诸如mixin之类的语言功能来解决表达问题,但尚未得到广泛接受的解决方案成为主流。


我喜欢你的回答,这里有智慧的话。几个月前我遇到了它,只是花了30分钟专门寻找它,因为我没有为其添加书签。对于那些了解理解概念而非技术优势的人来说,这是关于OOP与FP的最好解释。关于表达问题的论文也很棒。非常感谢您分享您的见解,我认为您的答案被低估了。
tobiak777 '16

4

没有真正的对。它们可以完全互补。FP语言支持OOP。但是社区在处理模块化方面的方式有所不同。

FP语言的用户倾向于通过数学定律实现模块化。并且更喜欢证明其法律合规的证据。

在命令式OOP中,用户倾向于在测试用例中捕获对象的行为,如果对象已更改,则可以重新运行该对象并以这种方式实现模块化。

这只是一个小方面,但我认为值得一提。


2

打个比方:

您已收到工作申请书。您填写姓名,联系方式和工作经历。完成后,您将不再有空白的应用程序。

现在想象一下,写之前先用透明的玻璃纸覆盖它。你写你的名字。您添加另一张玻璃纸。您写您的联系信息。玻璃纸。您写下您的工作记录。完成后,您仍然可以使用空白应用程序。您还拥有三张玻璃纸,每张都捕获了单个离散变化的效果。

前者(OOP)接受将事物更改为原位的想法,而后者(FP)则避免。两者都是状态管理范例。两者都可以使用不同的策略来捕获完成求职申请的效果。OOP直接更改启动工具,而FP覆盖之前发生的变化以影响更改的外观


美丽的比喻,谢谢!您是否介意(如果可能的话)在这两种方法中用利弊来扩展这种类比。
拉胡尔·阿加瓦尔
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.