Functional, Declarative, and Imperative Programming
什么术语和命令式编程的功能,declarative,均值?
在写这篇文章的时候,这一页上投票最多的答案是不精确的,并且混淆了声明性和命令性的定义,包括引用维基百科的答案。有些答案以不同的方式混淆了这些术语。好的。
另请参阅我对为什么电子表格编程是声明性的解释,不管公式会改变单元格。好的。
另外,一些答案声称函数式编程必须是声明性的子集。在这一点上,这取决于我们是否区分"功能"和"程序"。让我们先处理命令式和声明式。好的。
陈述式的定义好的。
唯一可能区分声明表达式和命令表达式的属性是其子表达式的引用透明性(RT)。所有其他属性要么在两种类型的表达式之间共享,要么从RT派生。好的。
100%的声明性语言(即每个可能的表达式都是RT的语言)不允许(除其他RT要求外)存储值的突变,例如HTML和大部分haskell。好的。
RT表达式的定义好的。
RT通常被称为"无副作用"。术语效应没有一个精确的定义,所以有些人不同意"没有副作用"与RT相同。RT有一个精确的定义。好的。
由于每个子表达式在概念上都是一个函数调用,rt要求函数的实现(即被调用函数内的表达式)不能访问函数外部的可变状态(允许访问可变的本地状态)。简单地说,函数(实现)应该是纯的。好的。
纯函数的定义好的。
纯函数通常被称为"无副作用"。"效应"这个词没有一个精确的定义,所以有些人不同意。好的。
纯函数具有以下属性。好的。
- 唯一可观察的输出是返回值。
- 唯一的输出依赖项是参数。
- 在生成任何输出之前,参数都是完全确定的。
记住,rt应用于表达式(包括函数调用),purity应用于函数(实现)。好的。
一个不纯净函数的模糊例子是并发性,但这是因为中断抽象层的纯度被破坏了。你真的不需要知道这个。要生成rt表达式,可以调用纯函数。好的。
RT的导数属性好的。
声明性编程引用的任何其他属性,例如维基百科使用的1999年的引用,要么来自RT,要么与命令式编程共享。从而证明我的准确定义是正确的。好的。
注意,外部值的不可变性是RT需求的一个子集。好的。
声明性语言没有循环控制结构,例如
for 和while ,因为由于不可变,循环条件永远不会改变。好的。声明性语言不表示嵌套函数顺序之外的控制流(也称为逻辑依赖关系),因为由于不可变性,计算顺序的其他选择不会更改结果(见下文)。好的。
声明性语言表示逻辑"步骤"(即嵌套的rt函数调用顺序),但每个函数调用是否是更高级别的语义(即"要做什么")不是声明性编程的要求。与命令的区别在于,由于不可变性(即更一般的rt),这些"步骤"不能依赖于可变状态,而只能依赖于表示逻辑的关系顺序(即函数调用的嵌套顺序,即a.k.a.子表达式)。好的。
例如,在对段落中的子表达式(即标记)进行评估之前,不能显示HTML段落
因此,不可变性的派生属性(更一般地说是rt)是声明性表达式,只表示组成部分(即子表达式函数参数)的逻辑关系,而不表示可变状态关系。好的。
估价单好的。
选择子表达式的计算顺序只能在任何函数调用不是rt(即函数不是纯函数)时给出不同的结果,例如,在函数内访问函数外部的某些可变状态。好的。
例如,对于一些嵌套表达式,例如
然而,如果函数
注意,嵌套表达式在概念上是嵌套函数,因为表达式运算符只是伪装成一元前缀、一元后缀或二进制中缀符号的函数调用。好的。
相切地说,如果所有标识符(如
顺便说一下,haskell有一个不同的语法,
评估订单详细信息好的。
函数是从输入到输出的状态转换(不是可变存储值)。对于纯函数调用的RT组合,这些状态转换的执行顺序是独立的。每个函数调用的状态转换都独立于其他函数,因为没有副作用,并且RT函数可以被其缓存值替换。为了纠正一个普遍的误解,纯一元合成总是声明性的和RT的,尽管事实上haskell的
"渴望计算"表示在调用函数之前对函数参数进行计算,而"懒惰计算"则表示在函数中访问参数(和if)之前不会对这些参数进行计算。好的。
定义:函数参数在函数定义站点声明,函数参数在函数调用站点提供。知道参数和参数之间的区别。好的。
从概念上讲,所有表达式都是函数调用的组成部分,例如常量是没有输入的函数,一元运算符是有一个输入的函数,二进制中缀运算符是有两个输入的函数,构造函数是函数,甚至控制语句(如
注意,没有图灵完整语言(即允许无限递归)是完全声明性的,例如,懒惰的评估引入了内存和时间不确定性。但是,这些由于选择评估顺序而产生的副作用仅限于内存消耗、执行时间、延迟、不终止和外部滞后,从而实现外部同步。好的。
函数式编程好的。
因为声明性编程不能有循环,所以迭代的唯一方法是函数递归。从这个意义上说,函数式编程与声明式编程有关。好的。
但是函数式编程并不局限于声明式编程。功能组合可以与子类型进行对比,特别是在表达式问题上,可以通过添加子类型或功能分解来实现扩展。扩展可以是两种方法的混合。好的。
函数式编程通常使函数成为第一类对象,这意味着函数类型可以出现在语法中任何其他类型的地方。其结果是函数可以输入和操作函数,从而通过强调函数组合(即分离确定性计算的子计算之间的依赖关系)来分离关注点。好的。
例如,对于可以应用于集合中每个元素的无限多的可能专用操作,函数编程不必编写单独的函数(如果函数也必须是声明性的,则使用递归而不是循环),而是使用可重复使用的迭代函数,例如
但是,请注意,如果函数不是纯函数,那么它实际上是一个过程。我们可能会争辩说,使用不纯函数的函数式编程实际上是程序式编程。因此,如果我们同意声明性表达式是rt,那么我们可以说过程编程不是声明性编程,因此我们可以说函数编程总是rt,必须是声明性编程的一个子集。好的。
平行性好的。
这种具有一级函数的函数组合可以通过分离独立函数来表示并行度的深度。好的。
Brent’s Principle: computation with work w and depth d can be
implemented in a p-processor PRAM in time O(max(w/p, d)).Ok.
并发性和并行性都需要声明性编程,即不可变性和RT。好的。
So where did this dangerous assumption that Parallelism == Concurrency
come from? It’s a natural consequence of languages with side-effects:
when your language has side-effects everywhere, then any time you try
to do more than one thing at a time you essentially have
non-determinism caused by the interleaving of the effects from each
operation. So in side-effecty languages, the only way to get
parallelism is concurrency; it’s therefore not surprising that we
often see the two conflated.Ok.
FP评估顺序
注意,评估顺序也会影响功能组成的终止和性能副作用。好的。
热切(cbv)和懒惰(cbn)是分类决斗[10],因为它们的评估顺序相反,即是先评估外部功能还是内部功能。设想一个颠倒的树,然后从函数树分支提示向上到最高级别的函数主干,而lazy则从主干向下到分支提示进行评估。"渴望"没有连接产物("和",A/K/A分类"产物"),而"懒惰"没有分离副产物("或",A/K/A分类"总和")[11]。好的。
性能好的。
渴望的好的。
与非终止的情况一样,EAGER对于连接函数组合过于渴望,也就是说,组合控制结构做了不必要的工作,而不是用LAZY来完成。例如,当整个列表由一个以第一个真正元素结尾的折叠组成时,热切地、不必要地将其映射到布尔值。好的。
这种不必要的工作导致了所谓的"最多"一个额外的log n因素,在连续的时间复杂度中,急切与懒惰,两者都有纯函数。一种解决方案是将函数(例如列表)与懒惰的构造函数一起使用(例如,使用可选的懒惰产品时,使用的是"渴望"),因为"渴望"的不正确性源于内部函数。这是因为积是构造类型,即在初始固定点上具有初始代数的归纳类型[11]好的。
懒惰好的。
与非终止一样,lazy对于析取的功能组合过于懒惰,也就是说,共生产的终结性可能比必要的晚出现,从而导致不必要的工作和延迟的不确定性,这与eagery不同[10][11]。终结性的例子有状态、计时、非终止和运行时异常。这些都是命令性的副作用,但即使在纯声明性语言(例如haskell)中,命令性IO monad中也存在状态(注意:并非所有monad都是命令性的!)在空间分配中,时间是相对于强制性现实世界的状态。使用lazy-even和可选的热切副产品会将"lazness"泄漏到内部副产品中,因为lazness的不正确源于外部函数(请参见非终止部分中的示例,其中==是外部二进制运算符函数)。这是因为副产品受最终性的限制,即在最终对象上具有最终代数的共生产类型[11]。好的。
Lazy在设计和调试延迟和空间的函数时会导致不确定性,由于声明的函数层次结构和运行时计算顺序之间的不一致,因此调试可能超出了大多数程序员的能力。Lazy Pure函数的计算结果为EAGER,可能会在运行时引入以前看不到的非终止。相反,用lazy计算的热切纯函数可能会在运行时引入以前看不见的空间和延迟不确定性。好的。
非终止好的。
在编译时,由于图灵完备语言中的停顿问题和相互递归,一般不能保证函数终止。好的。
渴望的好的。
对于
Head 和Tail 的结合,如果Head 或Tail 没有终止,则分别是List( Head(), Tail() ).tail == Tail() 或List( Head(), Tail() ).head == Head() 不是真的,因为左边没有终止,右边没有终止。好的。然而,随着懒惰的双方终止。因此,EAGER对于连接产品过于渴望,在不需要连接产品的情况下不终止(包括运行时异常)。好的。
懒惰好的。
由于懒惰而不热心,对于
1 或2 的分离,如果f 不终止,那么List( f ? 1 : 2, 3 ).tail == (f ? List( 1, 3 ) : List( 2, 3 )).tail 不是真的,因为左侧终止,右侧不终止。好的。然而,由于双方都不急于终止,因此永远无法达到平等测试。因此,lazy对于析取的副产品太懒了,在这些情况下,在做了比earchy所做的更多的工作之后,就无法终止(包括运行时异常)。好的。
[10]声明性延续和分类二元性,Filinski,第2.5.4节CBV和CBN的比较,以及SCL中的3.6.1 CBV和CBN。好的。
[11]声明性连续性和分类对偶性,Filinski,第2.2.1节产品和副产品,第2.2.2节终端和初始对象,第2.5.2节懒惰产品的CBV,第2.5.3节渴望副产品的CBN。好的。好啊。
这些并没有任何明确的客观定义。以下是我将如何定义它们:
命令-重点是计算机应该采取什么步骤,而不是计算机将做什么(例如,C,C++,Java)。
声明性的-重点是计算机应该做什么,而不是它应该怎么做(例如SQL)。
泛函-声明性语言的一个子集,主要关注递归
命令式和声明式描述了两种相反的编程风格。命令式是传统的"一步一步的食谱"方法,而声明式则更多地是"这是我想要的,现在你要知道怎么做了"。
这两种方法在整个编程过程中都会出现,即使是使用同一种语言和同一个程序。一般来说,声明性方法被认为是可取的,因为它使程序员不必指定那么多的细节,同时也减少了产生错误的机会(如果您描述了您想要的结果,并且一些经过良好测试的自动过程可以从中向后工作来定义步骤,那么您可能希望事情变得更可靠。韩寒必须手动指定每一步)。
另一方面,命令式方法提供了更多的低级控制——这是编程的"微管理方法"。这可以让程序员利用有关问题的知识给出更有效的答案。因此,以更具声明性的方式编写程序的某些部分并不罕见,但对于速度关键的部分则更为必要。
正如您可能想象的那样,编写程序所用的语言会影响到您的声明性——一种内置了"智能"的语言,在给定结果描述的情况下,可以计算出要做什么,它将允许一种比程序员需要先用命令式代码添加这种智能的方法更具声明性的方法。能够在顶部构建一个更具声明性的层。例如,Prolog这样的语言被认为是非常声明性的,因为它内置了一个搜索答案的过程。
到目前为止,您会注意到我没有提到函数式编程。这是因为它是一个术语,其含义与其他两个术语没有直接联系。最简单的函数式编程就是使用函数。特别是,您使用一种支持函数作为"第一类值"的语言——这意味着不仅可以编写函数,而且可以编写编写函数(编写函数…)并将函数传递给函数的函数。简而言之,这些函数和字符串和数字一样灵活和常见。
那么,功能性的、命令性的和声明性的经常被一起提到似乎很奇怪。其原因是将函数式编程的思想"推向极端"的结果。从最纯粹的意义上讲,函数是数学中的某种东西——一种接受某些输入并总是给出相同输出的"黑匣子"。这种行为不需要存储变化的变量。因此,如果你设计了一种编程语言,其目的是实现一个非常纯粹的、受数学影响的函数式编程思想,那么你最终在很大程度上拒绝了价值观的改变(在某种有限的技术意义上)。
如果你这样做——如果你限制变量的变化方式——那么几乎是偶然的——你最终迫使程序员编写更具声明性的程序,因为命令式编程的很大一部分是描述变量是如何变化的,你不能再这样做了!所以事实证明,函数式编程——特别是用函数式语言编程——倾向于给出更多的声明性代码。
总结一下,然后:
命令式和声明式是两种相反的编程风格(相同的名称用于鼓励这些风格的编程语言)
函数式编程是一种编程方式,函数变得非常重要,因此,改变值变得不那么重要。指定值更改的能力有限,强制使用更具声明性的样式。
所以"函数式编程"通常被描述为"声明性的"。
简而言之:
命令式语言指定计算机按顺序执行的一系列指令(先执行,然后再执行)。
声明性语言声明了一组关于输出应该从哪个输入得到什么结果的规则(例如,如果您有a,那么结果是b)。引擎将把这些规则应用于输入,并给出输出。
函数语言声明一组数学/逻辑函数,定义如何将输入转换为输出。它是一种声明性语言。
当务之急:如何实现我们的目标
1 2 3 | Take the next customer from a list. If the customer lives in Spain, show their details. If there are more customers in the list, go to the beginning |
声明性:我们想要实现的
1 | Show customer details of every customer living in Spain |
命令式编程是指任何一种编程方式,在这种方式下,程序是由描述计算机如何执行操作的指令构成的。
声明性编程指的是任何一种类型的编程,其中您的程序是问题或解决方案的描述,但没有明确说明如何完成工作。
函数编程是通过对函数和函数的函数进行评估来进行编程的。正如(严格定义的)函数式编程是指通过定义无副作用的数学函数来进行编程,因此它是声明式编程的一种形式,但它不是唯一一种声明式编程。
逻辑编程(例如在prolog中)是声明性编程的另一种形式。它涉及到通过判断逻辑语句是否为真(或是否可以满足)来进行计算。程序通常是一系列事实和规则,即描述而不是一系列指令。
术语重写(例如casl)是声明性编程的另一种形式。它涉及代数项的符号转换。它完全不同于逻辑编程和功能编程。
命令-表达式描述要执行的操作序列(关联)
声明性-表达式是有助于程序行为的声明(关联、交换、等幂、单调)
函数表达式只起作用;语义支持等式推理
自从我写了我之前的答案,我已经制定了一个声明性属性的新定义,引用如下。我还将命令式编程定义为双重属性。好的。
这个定义比我之前的回答中提供的定义要好,因为它简洁而且更一般。但这可能更难摸索,因为不完全性定理的含义,适用于编程和一般生活,是很难让人类的思想包罗万象。好的。
引用的定义解释讨论了纯函数编程在声明性编程中的作用。好的。
所有外来的编程类型都符合以下声明性和命令性分类法,因为下面的定义声称它们是双重的。好的。< Buff行情>
陈述式与命令式好的。
声明性属性是奇怪的、迟钝的,很难在技术上精确的定义中捕获,该定义仍然是一般的且不模棱两可的,因为它是一个幼稚的概念,即我们可以声明程序的含义(即语义),而不会产生意想不到的副作用。意义的表达和避免意料之外的影响之间存在着内在的张力,这种张力实际上源于编程和我们的宇宙的不完全性定理。好的。
将声明性定义为"要做什么"和命令性定义为"如何做"是过于简单化、技术上不精确和常常模棱两可的。一个模棱两可的情况是"什么"是"如何"在一个输出程序-编译器的程序。好的。
显然,使语言图灵完成的无边界递归在语义上也是类似的——不仅在计算的语法结构(即操作语义)中。这是一个逻辑上类似于g的例子。德尔定理——"任何一个完整的公理系统也是不一致的"。想想那句话的矛盾怪诞吧!它也是一个例子,演示了语义表达式没有可证明的界限,因此我们不能证明一个程序(和它的语义类似)终止了a.k.a.中止定理。好的。
不完全性定理源于我们宇宙的基本性质,如热力学第二定律所述,"熵(即独立可能性的熵)永远趋向于最大化"。程序的编码和设计从来没有完成过——它是活的!-因为它试图解决现实世界的需求,而现实世界的语义总是在变化,并趋向于更多的可能性。人类从不停止发现新事物(包括程序中的错误;-)。好的。
准确地、技术地捕捉到这个没有优势的奇怪宇宙中上述的理想概念(想想吧!我们的宇宙中没有"外部"的存在),需要一个简洁但不简单的定义,在被深入解释之前,这个定义听起来是不正确的。好的。
定义:好的。
The declarative property is where there can exist only one possible set of statements that can express each specific modular semantic.
Ok.
The imperative property3 is the dual, where semantics are inconsistent under composition and/or can be expressed with variations of sets of statements.
Ok.
声明性的定义在语义范围内具有明显的局部性,这意味着它要求模块化语义保持其一致的含义,无论它在何处以及如何在全局范围内实例化和使用。因此,每个声明性模块语义都应该与所有可能的其他语义本质上是正交的——而不是一个不可能(由于不完全性定理)的全局算法或模型来证明一致性,这也是卡内基梅隆大学计算机科学教授罗伯特哈珀关于"更多并不总是更好"的观点。标准ML设计师的E。好的。
这些模块化声明性语义的示例包括类别理论函数,例如
因此,设计良好的声明性语言可以更清楚地表达意义,尽管在可以表达的内容上失去了一些通用性,但是在可以用内在一致性表达的内容上获得了收益。好的。
上述定义的一个例子是电子表格程序单元格中的一组公式,当移动到不同的列和行单元格时,即单元格标识符发生更改时,这些公式不应具有相同的含义。单元格标识符是预期含义的一部分,并非多余。因此,对于一组公式中的单元格标识符,每个电子表格结果都是唯一的w.r.t.。在这种情况下,一致的模块化语义是使用单元格标识符作为单元格公式纯函数的输入和输出(见下文)。好的。
超文本标记语言A.K.A.HTML——静态网页的语言——是高度(但不完美)的声明性语言的一个例子,这种语言(至少在HTML5之前)不具备表达动态行为的能力。HTML可能是最容易学习的语言。对于动态行为,命令式脚本语言(如javascript)通常与HTML结合使用。不带javascript的HTML适合声明性定义,因为每个名义类型(即标记)在语法规则的组合下保持其一致的含义。好的。型
声明性的一个相互竞争的定义是语义语句的交换性和等幂性,即语句可以在不改变意义的情况下重新排序和复制。例如,如果这些名称是模块化的w.r.t.或任何隐含的顺序,那么为命名字段赋值的语句可以重新排序和复制,而不会改变程序的含义。名称有时意味着一个顺序,例如,单元格标识符包括其列和行位置-在电子表格中移动总计会改变其含义。否则,这些属性隐式地要求语义的全局一致性。通常不可能设计语句的语义,以便在随机排序或复制时保持一致,因为顺序和复制是语义固有的。例如,语句"foo exists"(或construction)和"foo does not exists"(和destruction)。如果考虑到预期语义的随机不一致性和特有性,那么就可以接受这个定义作为声明性属性的足够通用的定义。本质上,这个定义作为一个广义的定义是空虚的,因为它试图使一致性与语义正交,也就是说,它违背了这样一个事实,即语义的宇宙是动态无边界的,并且不能在一个全局一致性范式中被捕获。好的。型
对低级操作语义的(结构计算顺序)要求交换和等幂属性,将操作语义转换为声明性本地化模块语义,例如纯函数编程(包括递归而不是命令循环)。然后,实现细节的操作顺序不会影响(即在全球范围内传播)更高级别语义的一致性。例如,电子表格公式的计算顺序(理论上也是重复的)并不重要,因为在计算完所有输出(即类似于纯函数)之后,才能将输出复制到输入。好的。型
C,Java,C++,C语言,PHP和JavaScript并不是特别声明的。copute的语法和python的语法更具声明性地耦合到预期的结果,即一致的句法语义,消除外来语,使人容易在他们忘记代码之后理解代码。科普特和哈斯克尔执行操作语义的决定论和鼓励"不要重复"你自己"(干),因为他们只允许纯粹的功能模式。好的。型
2即使我们可以证明一个程序的语义,例如,使用语言coq,这也仅限于在键入时表示的语义,并且键入永远无法捕获程序的所有语义,即使对于没有图灵完成的语言,例如,使用html+css,也可能表示不一致的组合,从而语义未定义。好的。
3许多解释错误地声称只有命令式编程才有语法上有序的语句。我澄清了命令式编程和函数式编程之间的混淆。例如,HTML语句的顺序不会降低其含义的一致性。好的。< /块引用>
编辑:我在罗伯特·哈珀的博客上发表了以下评论:好的。
in functional programming ... the range of variation of a variable is a type
Ok.
取决于如何区分功能性和命令性编程,您在命令式程序中的"可分配"也可能对其可变性进行绑定的类型。好的。
我目前唯一喜欢的功能性的非混乱定义编程是a)作为第一类对象和类型的函数,b)优先使用循环上的递归和/或c)纯函数-即那些不会影响当记忆化时编程(因此完全功能化编程不存在于通用的表示语义中由于操作语义的影响,例如内存分配)。好的。
纯函数的等幂性意味着函数调用它的变量可以用它的值来代替,一般来说命令式程序的论据。纯函数似乎是声明性的w.r.t.到未完成的状态转换在输入和结果类型之间。好的。
但是纯函数的组合并不能保持这样的性质一致性,因为可以建立副作用模型(全局状态)纯函数编程语言中的命令式过程,例如哈斯克尔的IOMONAD,而且完全不可能防止在任何图灵完整的纯函数编程中这样做语言。好的。
正如我在2012年所写,这似乎与在最近的博客中,声明性编程是试图抓住这样一个概念:预期的语义永远不会不透明的不透明语义的例子是依赖于顺序,操作上对高级语义擦除的依赖语义层(例如,类型转换不是转换和具体化的泛型限制高级语义),并依赖于变量值编程语言无法检查(证明正确)。好的。
因此,我得出结论,只有非图灵完备的语言才能声明的。好的。
因此,声明性语言的一个明确和独特的属性可能是它的输出可以被证明遵循一些可枚举的生成规则。例如,对于任何特定的HTML程序(忽略口译员分歧的方式不同),这不是脚本(即图灵不完整)则其输出可变性可以是可枚举的或者更简洁地说,HTML程序是它的可变性。同样,电子表格程序是其输入变量。好的。
所以在我看来,声明性语言是无限递归,即每G?德尔第二不完全性定理自引用定理不能被证明。好的。
莱西·兰波特写了一部关于欧几里得的童话在G工作?del不完全性定理在数学证明中的应用在编程语言上下文中,通过在类型和逻辑(咖喱霍华德通信等)。好的。< /块引用>好啊。
命令式编程:告诉"机器"如何做某事,结果你想要发生的事情就会发生。
声明性编程:告诉"机器"你想要发生什么,并让计算机知道如何做。
命令式示例
1
2
3
4
5
6
7
8
9 function makeWidget(options) {
const element = document.createElement('div');
element.style.backgroundColor = options.bgColor;
element.style.width = options.width;
element.style.height = options.height;
element.textContent = options.txt;
return element;
}声明性示例
1
2
3 function makeWidget(type, txt) {
return new Element(type, txt);
}注意:区别不是简单、复杂或抽象。如前所述,区别在于如何与什么。
现在,新的焦点:我们需要旧的分类?过去的命令/声明/功能方面在分类通用语言方面是很好的,但在现在所有的"大语言"(如Java、Python、JavaScript等)都有一些选项(通常是框架)来表达"其他焦点",而不是其主要的(通常命令),并表示并行进程、声明性函数、lambda等。
因此,这个问题的一个很好的变体是"现在对框架进行分类有什么好处?"…一个重要的方面是我们可以标记为"编程风格"…
注重数据与算法的融合一个很好的例子来解释。正如你在维基百科上看到的jquery,
The set of jQuery core features — DOM element selections, traversal and manipulation —, enabled by its selector engine (...), created a new"programming style", fusing algorithms and DOM-data-structures
因此,jquery是关注"新编程风格"的最佳(流行)示例,它不仅是面向对象的,而且是"融合算法和数据结构"。jquery和电子表格一样具有一定的反应性,但不是"面向单元格",而是"面向DOM节点"…比较此上下文中的主要样式:
不融合:在所有"大语言"中,在任何函数/声明性/命令式表达式中,通常是数据和算法的"不融合",除了某些面向对象之外,这是严格代数结构观点的融合。
一些融合:所有经典的融合策略,在当今有一些框架使用它作为范例…数据流、事件驱动编程(或旧的特定于域的语言,如awk和xslt)就像用现代电子表格编程一样,它们也是反应式编程风格的例子。
大融合:是"jquery风格"…jquery是一种专门针对领域的语言,专注于"融合算法和DOM数据结构"。
ps:其他"查询语言",如xquery、sql(以pl作为命令表达式选项)也是数据算法融合示例,但它们是孤岛,不与其他系统模块融合…当使用find() 变体和规范子句时,spring是另一个很好的融合示例。这里有一些关于"类型"的好答案。
我提交了一些与函数式编程人群相关的更多"异域"概念:
- 领域特定语言或DSL编程:创建一种新的语言来处理手头的问题。
- 元编程:当你的程序写其他程序时。
- 进化式编程:建立一个不断改进自己的系统,或者生成更好的子程序。
声明性编程是通过在输入和输出之间表达一些永恒的逻辑来编程的,例如,在伪代码中,下面的示例将是声明性的:
1
2
3
4
5
6
7 def factorial(n):
if n < 2:
return 1
else:
return factorial(n-1)
output = factorial(argvec[0])我们在这里定义一个称为"阶乘"的关系,并将输出和输入之间的关系定义为该关系。在这里应该很明显,关于任何结构化语言,声明性编程都可以有一定的扩展。声明性编程的一个核心思想是不可变数据,如果您分配给一个变量,那么您只能这样做一次,然后再也不要这样做。另外,更严格的定义意味着可能根本没有副作用,这些语言有时被称为"纯声明性"。
命令式的相同结果是:
1
2
3
4
5
6 a = 1
b = argvec[0]
while(b < 2):
a * b--
output = a在这个例子中,我们表示输入和输出之间没有永恒的静态逻辑关系,我们手动更改内存地址,直到其中一个保持所需的结果。很明显,所有语言都允许声明性语义有一定的扩展,但并非所有语言都允许命令式语义,一些"纯"声明性语言允许副作用和突变。
声明性语言通常被说成指定"必须做什么",而不是"如何做",我认为这是一个误称,声明性程序仍然指定一个人必须如何从输入到输出,但在另一方面,您指定的关系必须是有效可计算的(重要术语,如果您不知道,请查阅)。另一种方法是非确定性编程,它实际上只是指定一个结果满足什么条件,然后您的实现将在试验和错误中耗尽所有路径,直到成功为止。
纯声明性语言包括haskell和pure prolog。从一个到另一个的滑动比例将是:纯Prolog、Haskell、OcAML、Struts/Lisp、Python、JavaScript、C、Perl、PHP、C++、帕斯卡尔、C、FORTRAN、汇编。
我认为你的分类法不正确。有两种相反的类型:命令式和声明式。函数只是声明性的一个子类型。顺便说一句,维基百科也陈述了同样的事实。
简而言之,编程风格越强调(要做的)抽象出(要做的)如何做的细节,这种风格就越被认为是声明性的。相反,命令是正确的。函数式编程与声明式风格相关联。