什么是声明式编程?[关闭]


193

我一直在几个不同的背景下听到这个词。它是什么?


7
您选择的答案是正确的(由绿色复选标记表示),这是不正确的。它没有定义什么将声明式编程与其对立面—命令式编程区分开。请考虑更改您的选择。
谢尔比·摩尔三世

3
是的,标记为正确的答案不正确。
Dermot

4
@ShelbyMooreIII还指定哪个答案是正确的,以便我们阅读!
vivek.m 2012年

@ vivek.m我今天提供了一个新答案
谢尔比·摩尔三世

Answers:


148

声明式编程是指您以编写代码的方式描述您要执行的操作,而不是您要执行的操作。由编译器来决定如何做。

声明性编程语言的示例是SQL和Prolog。


29
你还是要弄清楚“如何”告诉你要“做什么” :)电脑
哈森

7
@hasenj这个和其他答案并没有定义唯一不与命令式编程共享的属性 -这是不变性
谢尔比·摩尔三世

3
如果您能说出它与命令式编程(C,C ++,C#等语言)有何不同,那将是非常不错的,然后,读者可以更轻松地发现差异。
RBT

1
程序员:“我想去巴黎。” declarative(c):“您想如何到达那里?乘船航行?还是乘坐飞机飞行?也许,先航行一半,然后再沿那条路飞行?” 程序员:“我对它的完成方式不感兴趣。” 强制性(sql):“不用担心。我可以查询您需要的内容。” (这就是我所理解的答案
奈特

如果SQL支持非参照透明的表达式,该如何声明?
java-addict301

80

其他答案已经很好地解释了声明式编程的含义,因此,我仅提供一些示例说明为什么这样做可能有用。

上下文独立

声明式程序与上下文无关。因为它们仅声明最终目标是什么,而不声明达到该目标的中间步骤,所以同一程序可以在不同的上下文中使用。使用命令式程序很难做到这一点,因为命令式程序通常取决于上下文(例如隐藏状态)。

yacc作为一个例子。这是一个解析器生成器。编译器编译器,一种用于描述语言语法的外部声明性DSL,以便可以从描述中自动生成该语言的解析器。由于其上下文独立性,您可以使用这种语法来做许多不同的事情:

  • 为该语法生成​​C解析器(的原始用例yacc
  • 为该语法生成​​C ++解析器
  • 为该语法生成​​Java解析器(使用Jay)
  • 为该语法生成​​C#解析器(使用GPPG)
  • 为该语法生成​​一个Ruby解析器(使用Racc)
  • 为该语法生成​​树形可视化文件(使用GraphViz)
  • 只需对yacc源文件本身进行漂亮的打印,精美的格式设置和语法突出显示,并将其作为语言的语法规范包含在《参考手册》中

还有很多 …

优化

因为您没有规定计算机要采取的步骤和顺序,它可以更加自由地重新安排程序,甚至可以并行执行某些任务。一个很好的例子是用于SQL数据库的查询计划程序和查询优化器。大多数SQL数据库允许您显示其实际执行的查询与您要求它们执行的查询。通常,这些查询看起来一无所有互相喜欢。查询计划程序考虑了您甚至梦dream以求的事情:例如磁盘磁盘的旋转等待时间,或者某个完全不同的用户的某些完全不同的应用程序刚刚执行了类似的查询,而您所使用的表与一起努力,以致您努力工作以免加载已在内存中。

有一个有趣的权衡这里:该机拥有更加努力地工作,以找出如何做一些事情会比在命令式语言,但是当它确实算起来,它对于优化更多的自由和更多信息阶段。


23

松散地:

声明式编程倾向于:

  • 声明或声明性语句的集合,每个都有含义(通常在问题域中),可以独立和孤立地理解。

命令式编程倾向于:

  • 命令序列,每个命令执行一些操作;但在问题域中可能有意义,也可能没有意义。

结果,命令式样式可以帮助读者理解系统实际运行的机制,但是可能无法洞悉系统打算解决的问题。另一方面,声明式的样式可以帮助读者理解问题领域和系统对解决问题所采取的方法,但是对机械问题的了解较少。

真实的程序(甚至是那些使用最适合最终用户的语言编写的程序,例如ProLog或C)往往会在不同点上以不同程度呈现两种风格,以满足作品的不同复杂性和沟通需求。一种风格不优于另一种。它们只是为了不同的目的,就像生活中的许多事情一样,节制是关键。


那是正确的答案。上面没有喃喃自语
Krzysztof Wende

感谢您不仅回答了这个问题,而且还结合上下文和实用性来做到“像我5岁时一样解释”风格。极好的答案。
monsto

17

这是一个例子。

在CSS(用于设置HTML页面样式)中,如果您希望图像元素的高度为100像素,而宽度为100像素,则只需“声明”即可,如下所示:

#myImageId {
height: 100px;
width: 100px;
}

您可以将CSS视为声明性的“样式表”语言。

读取和解释此CSS 的浏览器引擎可以自由使图像显得如此高和如此宽。不同的浏览器引擎(例如IE引擎,Chrome引擎)将以不同方式实现此任务。

当然,它们的独特实现不是用声明性语言编写的,而是以汇编,C,C ++,Java,JavaScript或Python之类的过程性语言编写的。该代码是一堆要逐步执行的步骤(可能包括函数调用)。它可能会做一些事情,例如插值像素值并在屏幕上渲染。


11

很抱歉,但是我必须不同意其他许多答案。我想停止对声明式编程的定义的这种误解。

定义

子表达式的引用透明性(RT)是声明式编程表达式的唯一必需属性,因为它是不与命令式编程共享的唯一属性。

声明式编程的其他引用属性均来自此RT。请单击上面的超链接以获取详细说明。

电子表格示例

有两个答案提到了电子表格编程。如果电子表格编程(也称为公式)无法访问可变的全局状态,则它是声明性程序设计。这是因为可变的细胞的值是该整体式输入输出的的main()(整个程序)。在执行每个公式之后,新值不会写入单元格中,因此在声明性程序的生命周期(电子表格中所有公式的执行)中,它们是不可变的。因此,相对于彼此,公式将这些可变单元视为不可变的。允许RT函数访问不可变的全局状态(以及可变的本地状态)。

因此,当程序终止时(作为的输出main()),对单元格中的值进行突变的能力不会使它们在规则的上下文中可变。关键区别在于执行每个电子表格公式后不会更新单元格值,因此执行公式的顺序无关紧要。在执行所有说明性公式后,将更新单元格值。


1
意外的副作用会破坏声明的内容与程序的实际行为之间的关系。我在一个新答案中对此进行了更详细的解释。
谢尔比·摩尔三世

8

声明式编程是图片,其中命令式编程是绘制该图片的指令。

如果要“说明它是什么”,则是以声明方式进行的,而不是描述计算机到达所需位置所应采取的步骤。

当您使用XML标记数据时,您使用的是声明式编程,因为您在说“这是一个人,那是生日,在那儿有一条街道地址”。

将声明式和命令式编程结合在一起以产生更大效果的一些示例:

  • Windows Presentation Foundation使用声明性XML语法来描述用户界面的外观以及控件与基础数据结构之间的关系(绑定)。

  • 结构化配置文件使用声明性语法(如“键=值”对那样简单)来标识数据的字符串或值的含义。

  • HTML使用标记来标记文本,标记描述每个文本相对于整个文档所起的作用。


2
尽管XML是声明性的,但我不会说它是声明性编程,只是因为没有与标记关联的活动语义。说某事是一个地址并不能帮助您确定您要使用的地址。
HenryR

1
必须有一个使用声明式程序的基础上下文(域?)。因此,将XML与ANT结合使用可以解释为声明式程序。
Gutzofter

7

声明式编程是使用声明(即声明性语句)进行编程。陈述性语句具有许多使它们与命令性语句区分开的属性。特别是,声明是:

  • 可交换的(可以重新排序)
  • 关联的(可以重新组合)
  • 等幂的(可以重复而不改变含义)
  • 单调的(声明不减去信息)

一个相关的观点是,这些都是结构特性,并且与主题正交。声明式不是关于“什么与如何”。我们可以像声明“ what”一样容易地声明(表示和约束)“ how 声明式的是结构,而不是内容。声明性编程对我们如何抽象和重构代码以及如何将其模块化为子程序具有重大影响,但对域模型影响不大。

通常,我们可以通过添加上下文从命令式转换为声明式。例如,从“左转。(...等待...)右转。” 到“鲍勃将在11:01在Foo和Bar的交点处左转。鲍勃将在11:06在Bar和Baz的交点处右转。” 请注意,在后一种情况下,句子是等幂和可交换的,而在前一种情况下,重新排列或重复句子将严重改变程序的含义。

关于单调性,声明可以添加约束,从而减少可能性。但是约束仍然会添加信息(更确切地说,约束就是信息)。如果需要时变声明,通常使用显式的时间语义对其进行建模-例如,从“球平坦”到“时间T处平坦”。如果我们有两个相互矛盾的声明,那么我们将有一个不一致的声明系统,尽管可以通过引入约束(优先级,概率等)或利用超一致逻辑来解决。


1
声明式表达式有助于程序的预期行为,而命令式表达式则有助于计划或预期的行为。如果这是故意的语义,则声明式不必是可交换的和幂等的。
谢尔比摩尔三世

6

向计算机描述您想要的东西,而不是如何做某事。


6

想象一个excel页面。使用公式填充列以计算您的纳税申报单。

所有逻辑都在单元格中声明,计算顺序由公式本身确定,而不是程序确定。

这就是声明式编程的全部意义所在。您声明问题空间和解决方案,而不是程序流程。

Prolog是我使用的唯一声明性语言。它需要不同的思维方式,但是最好学习是否让您接触典型的过程编程语言之外的其他东西。


6

自2011年12月提供该问题的答案以来,我对声明式编程有了更深入的了解。这是我目前的理解。

我的理解(研究)的详细版本在此链接上进行了详细介绍,您应该阅读该链接以深入理解我将在下面提供的摘要。

命令式编程是存储和读取可变状态的地方,因此,程序指令的顺序和/或重复可以改变程序的行为(语义)(甚至导致错误,即意想不到的行为)。

在最幼稚和极端的意义上(我在先前的回答中断言),声明式编程(DP)避免所有存储的可变状态,因此,程序指令的顺序和/或重复不能更改的行为(语义) 。

但是,这样的极端定义在现实世界中并不是很有用,因为几乎每个程序都涉及存储的可变状态。该电子表格例子符合DP的这种极端的定义,因为整个程序代码运行到完成与输入状态中的一个静态副本,存储在新的状态之前。然后,如果任何状态发生更改,则重复此操作。但是大多数现实世界的程序不能仅限于这种状态变化的整体模型。

DP的一个更有用的定义是,编程指令的顺序和/或重复不会改变任何不透明的语义。换句话说,在语义上没有隐藏的随机变化-程序指令顺序和/或重复的任何变化仅导致程序行为的预期和透明变化。

下一步将是讨论哪些编程模型或范例有助于DP,但这不是这里的问题。


更新:请在我对声明式编程的定义的其他答复中也参考更详尽的解释。
谢尔比·摩尔三世

Functional programming如今,这是一个时髦的词,基本上是声明性编程的子集。当C#语言本身本质上是必不可少的时,C#语言中的LINQ是功能编程的一个元素。因此,按照该定义,C#成为一种混合动力。
RBT

1
compute.com链接已死。
Kedar Mhaswade

5

这是一种基于描述的编程方法 什么事应该做或可以代替描述如何它应该工作。

换句话说,您不会编写由表达式构成的算法,而只是布局想要的事物。HTML和WPF是两个很好的例子。

这篇Wikipedia文章是一个很好的概述:http : //en.wikipedia.org/wiki/Declarative_programming


1
轻微的小题词。WPF是一个库,不是真正的语言或范例。我认为您真的想说XAML是声明性语言的示例。
尼克,

您将如何描述使用库/框架进行编程?
Gutzofter

声明声明性编程不能包含表达式是不正确的。关键区别在于表达式不能使存储的值发生变化
谢尔比·摩尔三世

HTML不是一种编程语言
niahoo 2014年

5

自从我写了先前的答案以来,我为声明性属性制定了新的定义,下面对此进行了引用。我也将命令式编程定义为对偶属性。

这个定义优于我在先前的回答中提供的定义,因为它简洁明了,而且更笼统。但这可能更难以理解,因为不完整定理的含义适用于编程和一般的生活,这对于人类来说很难全神贯注。

引用的定义定义讨论了函数式编程在声明式编程中的作用。

声明式与命令式

声明性属性很奇怪,很钝,而且很难在技术上精确的定义中捕获,该定义仍然是通用且不明确的,因为这是一个天真的想法,我们可以声明程序的含义(即语义)而不会引起意外的副作用。在意义的表达与避免意想不到的效果之间存在着内在的张力,而这种张力实际上源于编程和宇宙的不完备性定理

将声明式定义为做什么和将命令式定义为如何做是过于简单化,技术上不精确且常常含糊不清。在输出程序的程序(编译器)中,“ 什么 ”是“ 如何 ”是一个模棱两可的情况。

显然,使图灵语言完整无限制递归在语义上也类似,不仅在评估的句法结构(即操作语义)中。从逻辑上讲,这是一个类似于哥德尔定理的例子-“ 任何完整的公理系统也都不一致 ”。思考那句话的矛盾怪异!这也是一个示例,说明了语义表达是如何没有可证明的界限的,因此我们不能证明2(或类似的语义)程序也就是停止定理而停止。

不完全性定理源于我们宇宙的基本本质,正如热力学第二定律所述,“ (也就是独立可能性的数量)正趋于永远最大化 ”。程序的编码和设计从未完成-它还活着!-因为它试图满足现实世界的需求,并且现实世界的语义总是在变化并且趋向于更多可能性。人类永远不会停止发现新事物(包括程序错误;-)。

为了在这个没有边缘的怪异宇宙中准确而技术地捕获上述期望的概念(思考!我们的宇宙没有“外面”),需要一个简短但看似不是简单的定义,在解释之前这听起来是不正确的深。

定义:


声明性属性是仅存在一组可能表示每个特定模块化语义的语句的地方。

命令属性3是双重的,其中语义在组成上是不一致的和/或可以用语句集的变体来表达。


声明性的这种定义在语义范围上是局部的,这意味着它要求模块化语义必须保持其一致的含义,而不管它在全局范围内的位置和方式如何被实例化和采用。因此,每个声明性模块化语义都应与所有其他可能语义上固有正交,而不是不可能的(由于不完全性定理)全局算法或模型,以证明一致性,这也是Robert Harper教授“ 更多并非总是更好 ” 的要点。卡耐基梅隆大学计算机科学专业的教授,是标准ML的设计师之一。

这些模块化声明性语义的实例包括类理论函子例如所述Applicative,标称打字,命名空间,命名字段,和WRT到语义的操作水平,那么纯功能的编程。

因此,经过精心设计的声明性语言可以更清楚地表达含义,尽管可以表达的内容失去了一般性,但是可以通过固有的一致性获得表达。

前述定义的一个示例是电子表格程序的单元格中的一组公式-当移至不同的列和行单元格(即,单元格标识符已更改)时,它们预期不会具有相同的含义。单元标识符是预期含义的一部分,并且并非多余。因此,每个电子表格结果对于一组公式中的单元格标识符都是唯一的。在这种情况下,一致的模块化语义是使用单元格标识符作为单元格公式的函数的输入和输出(请参见下文)。

超文本标记语言(又称HTML)(静态网页的语言)是一种高度(但不是完全3)声明性语言的示例,该语言(至少在HTML 5之前)没有表达动态行为的能力。HTML也许是最容易学习的语言。对于动态行为,通常将命令性脚本语言(如JavaScript)与HTML结合使用。没有JavaScript的HTML符合声明性定义,因为每种名义类型(即标签)在语法规则内的组成下都保持其一致的含义。

声明性的竞争定义是语义语句的可交换幂等属性,即,可以在不更改含义的情况下对语句进行重新排序和复制。例如,如果将名称分配给命名字段的语句是模块化的,则可以对其进行重新排序和重复,而无需更改程序的含义。名称有时暗示着一个顺序,例如单元格标识符包括它们的列和行位置-在电子表格上移动总计将更改其含义。否则,这些属性隐式要求全局语义的一致性。设计语句的语义通常是不可能的,因此如果随机排序或重复,则它们将保持一致,因为顺序和重复是语义固有的。例如,语句“ Foo存在”(或构造)和“ Foo不存在”(和破坏)。如果人们认为预期语义的地方随机性不一致,那么对于声明性来说,这一定义就足够普遍了。本质上,此定义作为通用定义是虚无的,因为它试图使一致性与语义正交,即无视语义世界是动态无界的,并且不能在全局一致性范例中捕获的事实。

需要低级操作语义(的结构评估顺序)的交换和幂等属性,将操作语义转换为声明性的局部模块化语义,例如函数式编程(包括递归而不是命令式循环)。这样,实现细节的操作顺序就不会影响(即,在全球范围内传播)高层语义的一致性。例如,电子表格公式的求值顺序(理论上也包括重复)并不重要,因为只有在计算完所有输出之后(即类似于纯函数),才将输出复制到输入中。

C,Java,C ++,C#,PHP和JavaScript并不是特别声明性的。Copute的语法和Python的语法在声明上与预期的结果相关联,即一致的语法语义消除了多余的内容,因此人们在忘记代码后可以很容易地理解它们。Copute和Haskell强制执行操作语义的确定性,并鼓励“ 不要重复自己 ”(DRY),因为它们只允许使用纯功能范式。


2即使我们可以证明程序的语义(例如使用Coq语言),也仅限于用打字表达的语义,而打字永远不能捕获程序的所有语义,即使是不是图灵完整的,例如使用HTML + CSS可以表达不一致的组合,从而具有不确定的语义。

3许多解释错误地声称仅命令式编程具有语法顺序的语句。我澄清了命令式和函数式编程之间的这种混淆。例如,HTML语句的顺序不会降低其含义的一致性。


编辑:我在罗伯特·哈珀的博客上发表了以下评论

在函数式编程中...变量的变化范围是一种类型

根据一个人如何将功能性与命令式编程区分开来,命令式程序中的“可分配值”也可能具有限制其可变性的类型。

我目前对函数式编程唯一了解的定义是a)作为一流对象和类型的函数,b)优先于循环的递归,和/或c)纯函数,即不影响所需语义的函数记住时的程序代码(因此,由于操作语义的影响(例如内存分配,在通用的指称语义中不存在完美的纯函数式编程)。

纯函数的幂等特性意味着可以用其值替换对变量的函数调用,而命令式命令的参数通常不是这种情况。对于输入类型和结果类型之间未组合的状态转换,纯函数似乎是声明性的。

但是纯函数的组成并不能保持任何这种一致性,因为可以用纯函数编程语言(例如Haskell的IOMonad)对副作用(全局状态)命令过程进行建模,而且完全不可能防止这种情况的发生。任何图灵完整的纯函数式编程语言。

正如我在2012年所写,似乎在您最近的博客中对评论的共识类似,声明式编程是一种尝试,旨在捕捉预期的语义永远都不透明的观念。不透明语义的示例有:依赖顺序,在操作语义层依赖于高层语义的擦除(例如,强制转换不是转换,而统一的泛型限制了高层语义),以及依赖于无法检查的变量值(已证明)正确)。

因此,我得出的结论是,只有非图灵完整语言才可以声明。

因此,声明性语言的一个明确而独特的属性可能是证明其输出可以服从一些可计数的生成规则集。例如,对于未编写脚本(即图灵不完整)的任何特定HTML程序(忽略解释器分歧的方式的差异),其输出可变性就可以枚举。或更简洁地说,HTML程序是其可变性的纯函数。同样,电子表格程序是其输入变量的纯函数。

因此在我看来,声明性语言是无界递归的对立面 ,即,根据哥德尔的第二不完全性定理,自指定理无法得到证明。

Lesie Lamport 了一个童话,讲述了Euclid可能如何围绕类型和逻辑(Curry-Howard对应关系等)的一致性,围绕哥德尔在编程语言上下文中应用于数学证明的不完全性定理进行工作。


4

声明式编程是“用符合开发人员心理模型而不是机器操作模型的语言进行编程的行为”。

通过解析结构化数据的问题很好地说明了声明式和命令式编程之间的差异。

命令式程序将使用相互递归的函数来使用输入并生成数据。声明性程序将表达一种语法,该语法定义了数据的结构,以便可以对其进行解析。

这两种方法之间的区别在于,声明性程序创建的新语言比其宿主语言更紧密地映射到问题的心理模型。


2

听起来可能很奇怪,但是我要在声明式系统列表中添加Excel(或任何电子表格)。这方面的一个很好的例子给出了这里


1

我会解释一下,因为DP是一种表达方式

  • 一个目标表达的条件-我们正在寻找。有一个,也许一个或多个?
  • 一些已知事实
  • 扩展已知事实的规则

...以及哪里有一个推演引擎,通常使用统一算法来找到目标。


-1

据我所知,它开始被用来描述像Prolog这样的编程系统,因为prolog(据说)是关于以抽象方式声明事物。

它的含义越来越小,因为它具有上述用户给出的定义。应该清楚的是,Haskell的声明式编程与HTML的声明式编程之间存在鸿沟。


1
在Haskell的声明式编程与HTML的声明式编程之间没有鸿沟,因为声明式编程的根属性是存储值的不变性。
谢尔比·摩尔三世

尽管如此,相对于图灵完整的编程系统,特定领域的语言之间甚至在其隐式计算中都受到限制,这之间存在相当大的差异。
Marcin

同意 图灵完整性与存储值的不变性正交。因此,我们不应将其与声明性与命令性属性混为一谈。感谢您考虑可能导致该“鸿沟”的属性之一(图灵完整性)。
谢尔比·摩尔三世

车削完成只需要无限制的递归。存储值的不变性不排除无限递归。因此,它们是正交属性。
谢尔比·摩尔三世

-2

声明式编程的其他两个示例:

  • 用于数据绑定的ASP.Net标记。例如,它只是说“用此源填充此网格”,然后将其留给系统以了解如何发生。
  • Linq表达式

声明式编程很好,因为它可以帮助简化您的思维模型代码的 *,并且最终可能更具可扩展性。

例如,假设您有一个函数会对数组或列表中的每个元素执行某些操作。传统代码如下所示:

foreach (object item in MyList)
{
   DoSomething(item);
}

没什么大不了的。但是,如果您使用更具声明性的语法,而是将DoSomething()定义为Action,该怎么办?然后您可以这样说:

MyList.ForEach(DoSometing);

当然,这更加简洁。但是我敢肯定,除了在这里和那里保存两行代码外,您还有更多的担忧。性能,例如。旧方法必须按顺序进行处理。如果.ForEach()方法可以让您发信号通知它可以自动并行处理,该怎么办?现在突然之间,您以一种非常安全的方式使代码成为多线程代码,并且只更改了一行代码。而且,实际上,.Net 有一个扩展,可以让您做到这一点。

  • 如果您点击该链接,它将带您到我的一个朋友的博客文章中。整个帖子有点长,但是您可以向下滚动到标题为 “问题” 的标题 _并在那里找到它。*


声明性编程可以发生变异存储的值......这只是您指定的(申报)有什么要发生变异,而不是究竟如何准确地去了解它变异。您还认为sql中的sql INSERT或UPDATE语句还有什么作用?
Joel Coehoorn 2013年

您错过了以下要点:如果您的函数不是纯函数,那么意外的副作用可能会破坏声明的内容与程序的实际行为之间的关系。我在一个新答案中对此进行了更详细的解释。
谢尔比·摩尔

-3

这取决于您如何提交文本答案。总的来说,您可以从某个角度查看程序,但这取决于您从哪个角度看问题。我将带您开始该计划:昏暗的公交车,汽车,时间,身高作为整数

同样,这取决于问题的整体所在。由于该程序,您可能不得不缩短它。希望这会有所帮助,如果没有帮助,则需要反馈。谢谢。

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.