大多数主流语言,包括诸如C#,Visual Basic,C ++和Java之类的面向对象编程(OOP)语言,都被设计为主要支持命令式(过程式)编程,而类似Haskell / gofer的语言纯粹是功能性的。谁能详细说明这两种编程方式之间的区别?
我知道选择编程方式取决于用户要求,但是为什么建议学习功能性编程语言呢?
大多数主流语言,包括诸如C#,Visual Basic,C ++和Java之类的面向对象编程(OOP)语言,都被设计为主要支持命令式(过程式)编程,而类似Haskell / gofer的语言纯粹是功能性的。谁能详细说明这两种编程方式之间的区别?
我知道选择编程方式取决于用户要求,但是为什么建议学习功能性编程语言呢?
Answers:
定义: 一种命令式语言使用一系列语句来确定如何实现某个目标。据说这些语句会改变程序的状态,因为依次执行每个语句。
示例: Java是命令式语言。例如,可以创建一个程序来添加一系列数字:
int total = 0;
int number1 = 5;
int number2 = 10;
int number3 = 15;
total = number1 + number2 + number3;
每个语句都会更改程序的状态,从为每个变量分配值到最终添加这些值。通过使用五个语句序列,可以明确地告诉程序如何将数字5、10和15相加。
函数式语言: 函数式编程范式已明确创建,以支持解决问题的纯函数式方法。函数式编程是声明式编程的一种形式。
纯函数的优点: 将函数转换实现为纯函数的主要原因是纯函数是可组合的:即自包含且无状态。这些特征带来了许多好处,包括:增强的可读性和可维护性。这是因为每个函数都设计为在给定参数的情况下完成特定任务。该功能不依赖任何外部状态。
简化迭代开发。由于代码易于重构,因此对设计的更改通常更易于实现。例如,假设您编写了一个复杂的转换,然后意识到某些代码在转换中重复了几次。如果通过纯方法进行重构,则可以随意调用纯方法而不必担心副作用。
简化测试和调试。因为可以更轻松地对纯函数进行隔离测试,所以您可以编写测试代码,以典型值,有效边缘情况和无效边缘情况来调用纯函数。
对于OOP People或命令式语言:
当您对事物进行固定的操作,并且随着代码的发展,您主要添加新的事物时,面向对象的语言会很好。这可以通过添加实现现有方法的新类来完成,而现有类则不予考虑。
当您拥有固定的事物集并且随着代码的发展,主要是在现有事物上添加新的操作时,功能语言会很好。这可以通过添加新功能来实现,这些新功能可以使用现有数据类型进行计算,而现有功能则可以单独使用。
缺点:
选择编程方式取决于用户要求,因此只有在用户未选择正确方式时才有危害。
当进化走错路时,您就会遇到问题:
区别在于:
当务之急:
...依此类推...
声明性的,其功能是子类别:
...依此类推...
摘要:使用命令式语言,您可以告诉计算机如何更改内存中的位,字节和单词以及更改顺序。在功能方面,我们告诉计算机什么是事物,动作等。例如,我们说0的阶乘为1,其他所有自然数的阶乘是该数与其前身的阶乘的乘积。我们不是说:要计算n的阶乘,请保留一个内存区域并在其中存储1,然后将该内存区域中的数字乘以2到n,然后将结果存储在同一位置,最后,存储区域将包含阶乘。
大多数现代语言在不同程度上都具有命令式和函数式的功能,但是为了更好地理解函数式编程,最好使用像Haskell这样的纯函数式语言的示例,而不是像Java / c#这样的功能性语言中的命令式代码。我相信通过示例进行解释总是很容易的,因此下面是一个示例。
功能编程:计算n的阶乘,即n!即nx(n-1)x(n-2)x ... x 2 X 1
-- | Haskell comment goes like
-- | below 2 lines is code to calculate factorial and 3rd is it's execution
factorial 0 = 1
factorial n = n * factorial (n - 1)
factorial 3
-- | for brevity let's call factorial as f; And x => y shows order execution left to right
-- | above executes as := f(3) as 3 x f(2) => f(2) as 2 x f(1) => f(1) as 1 x f(0) => f(0) as 1
-- | 3 x (2 x (1 x (1)) = 6
注意,Haskel允许函数重载到参数值的级别。现在下面是命令式代码的示例,它具有越来越高的命令性:
//somewhat functional way
function factorial(n) {
if(n < 1) {
return 1;
}
return n * factorial(n-1);
}
factorial(3);
//somewhat more imperative way
function imperativeFactor(n) {
int f = 1
for(int i = 1; i <= n; i++) {
f = f * i
}
return f;
}
该阅读资料可以很好地理解命令式代码如何更多地关注零件,机器状态(即for循环),执行顺序和流程控制。
后面的示例可以大致看作是Java / c#lang代码,而第一部分可以看作是语言本身的局限性,与Haskell会将函数按值(零)重载形成对比,因此,可以说这不是纯粹的函数式语言。你可以说它支持功能编。在某种程度上。
披露:以上代码均未经过测试/执行,但希望能够足以传达这一概念;我也将不胜感激任何这种纠正意见:)
return n * factorial(n-1);
吗?
n * (n-1)
函数式编程是声明性编程的一种形式,它描述了计算的逻辑,并且完全不强调执行的顺序。
问题:我想把这种生物从马变成长颈鹿。
每个项目可以以任何顺序运行以产生相同的结果。
命令式编程是程序性的。状态和秩序很重要。
问题:我想停车。
必须完成每个步骤才能获得所需的结果。在车库门关闭时将其拉入车库会导致车库门损坏。
函数式编程是“使用函数编程”,其中函数具有某些预期的数学特性,包括引用透明性。从这些特性出发,进一步的特性流动,特别是由可替代性实现的熟悉的推理步骤,这些步骤导致了数学证明(即证明结果的可信度)。
由此可见,功能程序仅是一种表达。
通过注意命令式程序中表达式不再是引用透明的位置(因此不是用函数和值构建的,并且本身不能成为函数的一部分)的位置,可以轻松查看两种样式之间的对比。最明显的两个地方是:突变(例如变量)其他副作用非本地控制流(例如例外)
在由功能和值组成的按程序表示的框架上,构建了语言,概念,“功能模式”,组合器以及各种类型的系统和评估算法的完整实用范例。
按照最极端的定义,几乎所有语言(甚至是C或Java)都可以称为功能性语言,但通常人们会保留具有特定相关抽象性(例如闭包,不变值和句法辅助工具(例如模式匹配))的术语。就使用函数式编程而言,它涉及使用functins并构建代码而没有任何副作用。用来写证明
从2005年到2013年,命令式编程风格一直在Web开发中得到实践。
通过命令式编程,我们逐步地写出了代码,该代码确切地列出了我们的应用程序应执行的操作。
函数式编程风格通过巧妙地组合函数来产生抽象。
答案中提到了声明式编程,关于这一点,我将说声明式编程列出了我们要遵循的一些规则。然后,我们向我们的应用程序提供所谓的某种初始状态,然后让这些规则定义应用程序的行为方式。
现在,这些快速描述可能没有多大意义,因此让我们通过类推来逐步了解命令式和声明式编程之间的区别。
想象一下,我们不是在构建软件,而是在烤馅饼。也许我们是糟糕的面包师,不知道如何以我们应该的方式烘烤美味的馅饼。
因此,我们的老板给了我们一份方向清单,这就是我们所知道的食谱。
食谱将告诉我们如何做馅饼。一种配方以命令式编写,如下所示:
声明式配方将执行以下操作:
1杯面粉,1个鸡蛋,1杯糖-初始状态
规则
因此,命令式方法的特征在于逐步方法。您从第一步开始,然后转到第二步,依此类推。
您最终会得到一些最终产品。因此,制作这块馅饼时,我们将这些配料混合在一起,放入锅中和烤箱中,即可得到最终产品。
在声明性世界中,它是不同的。在声明性配方中,我们将我们的配方分为两个独立的部分,从列出配方初始状态(如变量)的一个部分开始。因此,这里的变量是成分的数量及其类型。
我们采用初始状态或初始成分,并对它们应用一些规则。
因此,我们采用初始状态,并一遍又一遍地通过这些规则,直到可以立即食用大黄草莓派或其他任何东西为止。
因此,在声明式方法中,我们必须知道如何正确构造这些规则。
因此,我们可能要检查成分或状态的规则(如果混合),将它们放入锅中。
在我们的初始状态下,这是不匹配的,因为我们尚未混合成分。
因此规则2说,如果不混合,则将其混合在碗中。好的,这条规则适用。
现在我们有一碗混合配料作为我们的状态。
现在,我们再次将该新状态应用于规则。
因此,规则1指出,如果将各种配料混合在一起放在锅中,是的,现在规则1确实适用,那就去做吧。
现在,我们有了一个新的状态,可以在锅中将原料混合在一起。规则1不再相关,规则2不适用。
规则3说,如果将食材放在锅中,将它们放入烤箱,那么该规则就是适用于这种新状态的,那就做吧。
最后我们得到了美味的热苹果派或其他任何东西。
现在,如果您像我一样,您可能正在思考,为什么我们仍不进行命令式编程。这是有道理的。
是的,对于简单的流程,是的,但是大多数Web应用程序具有更复杂的流程,而命令式编程设计无法正确捕获这些流程。
在声明性方法中,我们可能具有一些初始成分或初始状态textInput=“”
,例如单个变量。
也许文本输入开始时是一个空字符串。
我们采用此初始状态,并将其应用于您的应用程序中定义的一组规则。
如果用户输入文本,请更新文本输入。好吧,现在那不适用。
如果呈现了模板,请计算小部件。
嗯,这都不适用,因此程序将只等待事件发生。
因此,有时用户会更新文本输入,然后我们可能会应用规则编号1。
我们可能会将其更新为 “abcd”
所以我们只是更新了text和textInput更新,规则2不适用,规则3说如果是刚刚发生的文本输入更新,则重新渲染模板,然后回到规则2,即是否渲染了模板,计算窗口小部件,好的,让我们计算窗口小部件。
总的来说,作为程序员,我们想争取更具声明性的编程设计。
强制性命令看起来更清晰明显,但是声明性方法可以很好地扩展到较大的应用程序。
我认为有可能以命令式方式表达函数式编程:
if... else
/ switch
语句 这种方法存在很大的问题:
解决诸如我认为的那些问题的功能编程,像对象一样对待函数/方法,并拥抱无状态。
用法示例:前端应用程序,例如Android,iOS或Web应用程序的逻辑(包括)。与后端通信。
使用命令性/过程代码模拟功能编程时的其他挑战:
我也相信,归根结底,功能代码将被编译器编译为命令式或过程式的汇编或机器代码。但是,除非您编写汇编程序,否则就像人类用高级/人类可读语言编写代码一样,对于列出的场景,函数式编程是更合适的表达方式。