首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

我应该追求哪个,函数式编程还是面向对象编程?

在函数式编程中,函数是头等对象即头等函数,这意味着一个函数,既可以作为其它函数的输入参数值,也可以从函数中返回值,被修改或者被分配给一个变量。λ演算是这种范型最重要的基础,λ演算的函数可以接受函数作为输入参数和输出返回值。比起指令式编程,函数式编程更加强调程序执行的结果而非执行的过程,倡导利用若干简单的执行单元让计算结果不断渐进,逐层推导复杂的运算,而不是设计一个复杂的执行过程。

阿隆佐·邱奇在1930年代开发的λ演算,是建造自函数应用的一种计算形式系统。在1937年,艾伦·图灵证明了λ演算和图灵机是等价的计算模型,展示了λ演算是图灵完备性的。λ演算形成了所有函数式编程语言的基础。另一种等价的理论公式化是组合子逻辑,它由Moses Schönfinkel和哈斯凯尔·柯里在1920年代和1930年代开发。邱奇后来又开发了简单类型λ演算,它通过向所有的项指定一个类型而扩展了λ演算。这个系统形成了静态类型函数式编程的基础。于20世纪50年代后期,John McCarthy在麻省理工学院,开发了早期的函数式语言LISP,运行在大型IBM主机(IBM700/7000系列)上。LISP的函数定义借鉴了邱奇的λ表示法,并扩展了标签构造来允许递归函数。最开始的LISP是多范型语言,并且随着新的范型的发展,越来越多的编程风格得到了支持。后来发展出来的方言比如Scheme、Clojure,和分支语言比如Dylan等,试图围绕一个清晰的函数式核心,来得出简化和理性化的LISP,而Common Lisp旨在保留并更新它所替代的各种更早先LISP方言的那些范型特征。

而于1956年发明的IPL语言,一般被认为是第一个基于计算机的函数式编程语言。它是一种用于操纵符号列表的汇编式语言。它有一个生成器的概念,相当于一个接受函数作为参数的函数,并且,由于它是汇编级语言,代码可以是数据,因此IPL可以被视为具有高阶函数。但是,它在很大程度上依赖于改变列表的结构和类似的指令式编程特征。在1960年代早期,Kenneth E. Iverson开发了APL语言,在他1962年出版的《A Programming Language》一书中对其有所介绍。APL对John Backus的FP语言施加了巨大的影响。在20世纪90年代早期,Iverson和Roger Hui创造了J语言。在20世纪90年代中期,以前曾与Iverson合作过的Arthur Whitney创建了K语言,后者在金融行业中与其派生出来的Q语言一起被商业化使用。

1977年John Backus在他的图灵奖颁奖演讲《编程可以从冯·诺依曼式风格中解放出来吗?一种函数式风格及其程序代数》中,展示了他提出的FP。他将函数式编程定义为通过“组合形式”以分层方式构建,允许“程序代数”; 在现代语言中,这意味着函数式程序应遵循复合性原理。Backus的论文推广了函数式编程的研究,虽然它强调的是函数级编程而不是现在所说的λ演算风格。1973年爱丁堡大学的Robin Milner发明了ML语言,它的语法受到了ISWIM的启发。同年,David Turner在圣安德鲁斯大学开发了SASL语言,它基于了ISWIM的应用式子集。在1976年,Turner重新设计并重新实现它为惰性求值语言。在20世纪70年代的爱丁堡,Rod Burstall和John Darlington开发了NPL语言。NPL基于Kleene的递归方程,并在他们的程序转换工作中首次引入。然后Rod Burstall、David MacQueen和Don Sannella结合了来自ML的多态类型检查,从NPL派生出了Hope语言。ML最终发展成几种语言,其中最常见的是OCaml和Standard ML。

在1970年代,Guy L. Steele和Gerald Jay Sussman开发了Scheme,如有影响力的“λ论文集”和经典的1985年教科书《计算机程序的构造和解释》中所描述的那样。Scheme是使用词法作用域和尾调用优化的第一个Lisp方言,将函数式编程的影响力提升到更广泛的范围,让更多的编程语言社区接触到它们。在1980年代,佩尔·马丁-洛夫开发了直觉类型论(也称为构造类型论),它将函数式编程与表现为依赖类型的数学证明联系起来。这导致了交互式定理证明的新方法的产生,并影响了后续的函数式编程语言的发展。在1985年David Turner开发的惰性求值函数式语言Miranda出现,它采用了来自ML与Hope语言的概念,作为他先前所设计的SASL和KRC语言的后继者。Miranda对后来的Haskell有很强的影响,由于它当时是专有软件,所以Haskell社区于1987年开始达成共识,以形成函数式编程研究的开放标准,对标准的实现自1990年以来一直在进行中。最近,它在基于CSG几何框架构建的OpenSCAD语言的参数CAD中得到了应用,虽然在重新赋值上的限制(所有值都被当作常量),导致了不熟悉函数式编程的用户混淆

任何图灵完备的语言都可以用来生成程序来解决任何可计算的问题。这是一个非常有限的概念。

编程语言不能解决问题。人们解决问题。计算机可以提供帮助。创建编程语言是为了使人类能够思考问题,表达他们对那些问题的想法,将他们的想法传达给数字计算机,以操纵计算机来解决问题。前两点——思想和表达——两种范式的处理方式截然不同。它们具有不同的优势,它们各自(按照目前的理解和实施方式)使某些思维和表达方式变得容易,而另一种则使对方变得困难。肯定有一些思考和表达解决方案的方式是 OOP做不到的。还有一个特定的概念是 OOP 的绝对核心,而 FP 还没有明确的叙述(尽管现在有一些非常有用的概念可以使用)。

函数式编程的核心是 Lambda 微积分。许多函数式编程语言的语义和句法都基于微积分的一个版本。这些语言大多编译到中间阶段,这些中间阶段是微积分的非常直接的表达(在某些情况下,几个阶段具有不同的改进)。编译器在过程中的许多步骤中使用来自微积分的过程(例如 eta 转换)。例如,构建抽象语法树(早期阶段)和执行编译器优化(后期阶段)。人类可以使用微积分来推理和重构他们的代码。例如,它可以用于简化代码或使其更加通用和参数化,并确信代码的基本功能没有改变。人类可以使用微积分来构建类型系统,并推断向程序中添加新类型将如何影响整体(以及它是否完全兼容)。人类可以使用这些类型系统来构建具有逻辑、可证明关系的整个抽象层次结构。一个结果是人类可以使用相同的原则在许多层面上一致地推理他们的代码。在他们编写的较高级别的代码或其使用的类型中,以及在运行时程序的低级别创建以及介于两者之间的所有点(如果使用完整的功能语言。

即使在非 FP 语言中应用 FP 风格,这些原则仍然可以应用于您的代码(只是需要额外的纪律来编写引用透明的代码)。我已经描述了 FP 可以做的事情和 OOP 甚至不尝试做的事情的列表,但我还没有解释什么是 Lambda 微积分。它是一个完整的计算模型。与图灵机一样,任何可计算的东西都可以在模型中表达。任何可计算的问题都可以用它来解决。(好吧,图灵和丘奇在正确定义“可计算”时都被吓坏了。到目前为止,我们都已经摆脱了它)。这也是一个易于访问的模型。我以一种她能理解的方式向我母亲解释。甚至不会尝试使用图灵机。因此,FP 提供的帮助人们思考代码的概念是基于计算模型的基础。FP 提供了一种一致的方法来在解决方案的每个级别进行推理(嗯,直到您到达最底层,当前的数字计算机设计意味着它必须转换为命令式的东西)。如果您愿意,FP 是一种全栈范式。一路往下计算。

OOP的起源和基础是什么?

这不是图灵机。该死,谁把他们的语言建立在无限来回的纸带上?这什么时候能帮助你思考你的代码和你试图解决的问题?对于语言设计,图灵机的意义在于它可以解决任何可计算的问题。如果可以证明你的语言可以创建一个模拟图灵机,那么它至少有一种方法可以解决任何问题,因为你总是可以将模拟磁带送入模拟机器。但是如果你对模拟机器感兴趣......OOP 起源于 Simula 67。创建 Simula 是为了提供一种对“复杂的人机系统”进行建模并将这些模型转换为编译代码的方法;一种语言来做这两种事情。为此创建了类、子类和对象。

有趣的是,他们在单线程语言中面临的一个严峻挑战是表示多个进程并发发生的任何复杂系统。所以他们添加了并发原语(准确地说是协程)。具有讽刺意味的是,第一个 OOP 语言有一个并发解决方案,鉴于 OOP 语言现在使并发变得困难的声誉(不完全公平——命令式使并发不同。OOP 只是给人一种让它变得容易的错觉)。受 Simula 影响的语言设计者大多忘记了并发问题。建模——以及将模型映射到代码单元——成为定义。OOP的核心是通过类、子类和对象进行域建模。

封装作为建模状态的一种方式。

一种评估策略,它阐明了领域模型并能够编译成一个工作程序。

就是这样。我不是在贬低成就;这是很多非常聪明,一致的“它”。

OOP 不是规定性的。它为域建模提供了一个灵活的框架,然后就交给您了。它为您的计算代码的评估和组织提供了最小的约束,其余的由您决定。它真的不在乎主要工作是由图灵机还是由踢踏舞的青蛙命令式地、功能性地完成的。大多数 OO 语言都是命令式语言的重新设计,因此您往往会发现它可以填补空白,但不一定非要这样。看看 OO 语言增加 FP 特性的速度。与 FP 不同,OOP 没有递归解释所有部分的通用底层模型。这是一个框架;一组似乎可以很好地协同工作的概念。除此之外,这在很大程度上取决于您的常识和经验。这就是四人组设计模式、SOLID 和其他设计模式的真正含义。OOP 不是全栈范例。有一个水平低于它没有用处(比 FP 停止的点高得多)。它无法帮助您优化编译后的代码或分析单个表达式。

OOP 有什么不好?

它根本不能做什么?自下而上递归定义和扩展自身。功能强大的新概念经常出现在 FP 世界中,它们完全、可证明地与(通常源自)现有抽象一致。面向对象和函数式编程不一定彼此不同。两者都有可以一起应用的重要原则。面向对象的编程更侧重于模块,并且能够在东西周围设置墙。这通常被称为“实现隐藏”。你暴露的一切,你都必须维护和支持。所以在面向对象的编程中,有很大的努力让一切都尽可能的私有。如果该对象不公开其实现细节,则该对象的用户无法利用这些实现细节,因此作为该对象(或更准确地说是类)的维护者,我可以在不破坏代码的情况下更改实现的私有部分其他一些使用我的对象/类的开发人员。

函数式编程更侧重于可验证性。作为一个有时被遗忘的结果,并行化。由于超出我正在回答的问题范围的原因,并行化功能程序更容易。但这些优势几乎相互独立。那么,为什么有人不开发一种我们同时进行的多范式编程语言呢?好吧,其中有几个。也许最流行的是 Scala 语言。您无法真正开发非面向对象的 Scala 程序。而且您几乎无法开发(大部分)没有功能的 Scala 程序。因此,如果您学习 Scala 编程,您将同时学习这两者!!!

需要注意的是,Scala 确实有其缺陷。这可能会令人困惑。但它是一种设计精良的语言,它会教给您函数式和面向对象的原理。在 Scala 之后,您可以毫不费力地转向像 Haskell 这样的纯函数式语言,或者您可以毫不费力地转向像 Ruby 这样的传统面向对象语言。为什么 Scala 和 F# 混合范式?好吧,他们所做的一件事就是尝试将 FP 的优势与 OO 熟悉的领域建模叙述相结合。另一个是让大量现有的技术、工具和库可用于函数式编程。

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20230124A00ZHP00?refer=cp_1026
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券