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

评论

一个人不拒绝另一个人。

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

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

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

真正的答案是-他们之间没有“对立”。在StackOverflow上查看此问题。

#1 楼

我想说的是,函数式编程比命令式编程更重要。换句话说,函数式编程仅使用表达式,而在命令式编程中则同时使用表达式和语句。

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

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

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的演示“用函数编程来驯服效果”,以很好地介绍函数概念。

评论


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

–替代
2010-10-5 22:36



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

– Lie Ryan
10-10-6在10:19

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

– CodexArcanum
2010年11月1日于15:05

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

–萨拉
16年5月1日在18:33

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

–代
17-10-5在9:38

#2 楼

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

一些示例包括引用透明性,lambda函数,一流函数,懒惰和急切的求值以及不变性。

如果没有别的,学习函数式编程对于那些它包含。它将改变您进行编程和思考编程的方式。而且我想未来的功能编程将和面向对象的编程一样重要。

开始使用时,您可以选择使用纯函数式语言,例如Haskell,也可以使用像F#这样的混合语言。

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



函数式编程与面向对象的编程有什么优缺点?


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

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

评论


需要明确的是,Scheme绝不是一种纯粹的功能语言。

–乔纳森·斯特林(Jonathan Sterling)
2010年10月6日,1:11

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

–missingfaktor
2010-10-16 15:02

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

–布赖恩·邦迪(Brian R. Bondy)
2010-10-16 22:24



您应该阅读以下问题的答案:stackoverflow.com/questions/3949618/fp-and-oo-orthogonal / ...

–missingfaktor
10-10-17在4:28

也可以在这里查看Frank Shearar的答案:programmers.stackexchange.com/questions/12423/…

–missingfaktor
2010-10-17 4:57

#3 楼

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


面向对象的语言在您对事物有固定的操作集时会很好,并且随着代码的发展,您主要会添加新的事物。这可以通过添加实现现有方法的新类来完成,而现有类则不予考虑。
当您拥有一组固定的东西时,功能语言会很好,并且随着代码的发展,您主要在以下位置添加新的操作:现有的东西。这可以通过添加可以使用现有数据类型进行计算的新函数来完成,而现有函数将被单独保留。

如果进化错误,您就会遇到问题:


向面向对象的程序添加新操作可能需要编辑许多类定义以添加新方法。
向功能程序添加新事物可能需要编辑许多函数定义以添加新方法案例。

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

评论


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

–tobiak777
16 Jan 15'20:48



#4 楼

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

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

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

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

#5 楼

打个比方:

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

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

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

评论


美丽的比喻,谢谢!您是否介意(如果可能的话)在这两种方法中扩展这种类比的利弊。

–rahulaga_dev
18年5月5日在9:44