声明式编程和命令式编程有什么区别?

我一直在寻找networking寻找一个声明式和命令式编程的定义,这将给我留下一些亮光。 然而,在我发现的一些资源中使用的语言令人望而生畏,例如在维基百科 。 有没有人有一个真实世界的例子,他们可以告诉我,这可能会给这个问题带来一些观点…也许在C#中。

一个很好的C#声明式与命令式编程的例子是LINQ。

使用命令式编程,您可以一步一步地告诉编译器您想要发生的事情。

例如,让我们从这个集合开始,select奇数:

List<int> collection = new List<int> { 1, 2, 3, 4, 5 }; 

使用命令式编程,我们将逐步完成这个任务,然后决定我们想要的:

 List<int> results = new List<int>(); foreach(var num in collection) { if (num % 2 != 0) results.Add(num); } 

在这里,我们说:

  1. 创build一个结果集合
  2. 浏览集合中的每个数字
  3. 检查数字,如果是奇数,则将其添加到结果中

另一方面,使用声明性编程,您可以编写描述所需内容的代码,但不一定如何获取它(声明所需的结果,但不是一步一步的):

 var results = collection.Where( num => num % 2 != 0); 

在这里,我们说“给我们所有的东西都是奇怪的”,而不是“单步执行收集。检查这个项目,如果它很奇怪,将它添加到结果集合中”。

在许多情况下,代码也是两种devise的混合体,所以它并不总是黑白的。

声明式编程就是当你说出你想要的东西的时候,命令式的语言就是当你说出如何得到你想要的东西的时候。

Python中的一个简单例子:

 # Declarative small_nums = [x for x in range(20) if x < 5] # Imperative small_nums = [] for i in range(20): if i < 5: small_nums.append(i) 

第一个例子是声明式的,因为我们没有指定任何构build列表的“实现细节”。

为了配合C#的例子,一般来说,使用LINQ的结果是声明式的风格,因为你不是说如何获得你想要的; 你只是在说你想要的东西。 你可以说相同的SQL。

声明式编程的一个好处就是它允许编译器做出可能比你手工编写更好的代码的决定。 使用SQL示例运行,如果您有类似的查询

 SELECT score FROM games WHERE id < 100; 

SQL“编译器”可以“优化”这个查询,因为它知道id是一个索引字段 – 或者它可能不是索引的,在这种情况下,它将不得不迭代整个数据集。 或者,也许SQL引擎知道,这是利用所有8个核心进行快速并行search的最佳时机。 作为一名程序员,您并不关心这些情况,您也不必编写代码来处理任何特殊情况。

说明性与必要性

编程范式是计算机编程的基本风格。 有四个主要范式:命令式,声明式,function性(被认为是声明范式的一个子集)和面向对象的。

声明性编程 :是一种编程范式,它expression一个计算的逻辑(什么),而不描述它的控制stream(怎么做)。 一些众所周知的声明性领域特定语言(DSL)示例包括CSS,正则expression式和SQL的子集(例如,SELECT查询)。许多标记语言(如HTML,MXML,XAML,XSLT等)通常都是声明式的。 声明式编程试图模糊作为一组指令的程序与作为关于期望的答案的断言的程序之间的区别。

命令式编程 :是一种编程范式,用于描述改变程序状态的语句的计算。 声明性程序可以被双重视为编程命令或math断言。

函数式编程:是一种将计算看作math函数的评估并避免状态和可变数据的编程范例。 它强调function的应用,与强调状态变化的命令式编程风格形成鲜明对比。 在一个纯粹的函数式语言中,比如Haskell,所有的函数都没有副作用,而状态变化只是表示为转换状态的函数。

以下MSDN中命令式编程的例子,循环遍历数字1到10,并find偶数。

 var numbersOneThroughTen = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; //With imperative programming, we'd step through this, and decide what we want: var evenNumbers = new List<int>(); foreach (var number in numbersOneThroughTen) { if (number % 2 == 0) { evenNumbers.Add(number); } } //The following code uses declarative programming to accomplish the same thing. // Here, we're saying "Give us everything where it's odd" var evenNumbers = numbersOneThroughTen.Select(number => number % 2 == 0); 

两个例子都有同样的结果,一个既不好也不差。 第一个例子需要更多的代码,但是代码是可testing的,并且命令式的方法可以完全控制实现细节。 在第二个例子中,代码可以说是更可读的; 然而,LINQ并不能控制幕后发生的事情。 你必须相信LINQ将提供所要求的结果。

我将添加另一个很less出现在声明式/命令式编程讨论中的例子:用户界面!

在C#中,您可以使用各种技术构buildUI。

在必要的结束,你可以使用DirectX或OpenGL来非常强制地绘制你的button,checkbox等…逐行(或者真的,三angular形三angular形)。 这是由你来说如何绘制用户界面。

在声明结束,你有WPF。 你基本上写了一些XML(是的,是的,技术上“XAML”),框架为你做的工作。 你说什么用户界面看起来像。 这是由系统决定如何做到这一点。

无论如何,还有一件事要考虑。 仅仅因为一种语言是说明性的或者说必要的,并不意味着它不具有另一种语言的某些特征。

另外,声明式编程的一个好处是通过阅读代码可以更容易理解目的,而命令则可以更好地控制执行。

这一切的主旨:

声明 – >你想做什么

势在必行 – >你想how

命令式编程要求开发人员逐步定义如何执行代码。 为了给出一个必要的方向,你说:“去第一街,左转到Main,开两个街区,右转到枫树,然后到左边的第三个房子。”声明版本可能听起来像这样:“开车去苏家。”一个人说怎么做, 另一个说什么需要做。

声明式风格与命令式风格相比有两个优点:

  • 它并不强迫旅行者记住一长串的指示。
  • 它允许旅行者尽可能地优化路线。

Calvert,C Kulkarni,D(2009)。 基本的LINQ。 艾迪生韦斯利。 48。

以上所有答案和其他在线post都提到以下内容

  • 使用声明式编程,您可以编写描述您所需内容的代码,但不一定知道如何获取它
  • 你应该更喜欢命令式编程的声明式编程

他们没有告诉我们是如何实现的 。 为了使程序的一部分更具说明性,其他部分必须提供抽象来隐藏实现细节(这是必要的代码)。

  • 例如,LINQ的用法比使用循环(for,while等)更具说明性,例如,我们可以使用list.Where()来获得一个新的过滤列表。 为了这个工作,微软已经完成了LINQ抽象背后的所有重要工作。

事实上,函数式编程和函数式库更具说明性的原因是因为它们已经抽象出循环和列出创build,隐藏了幕后的所有实现细节(最可能是循环的强制性代码)。

在任何程序中,你总是有命令性的和声明性的代码,我们应该瞄准的是隐藏抽象背后的所有命令代码,以便程序的其他部分可以声明性地使用它们。

最后,虽然函数式编程和LINQ可以使你的程序更具说明性,但是通过提供更多的抽象,你可以使它更具说明性。 例如:

 // JavaScript example // Least declarative var bestProducts = []; for(var i = 0; i < products.length; i++) { var product = products[i]; if (product.rating >= 5 && product.price < 100) { bestProducts.push(product); } } // More declarative var bestProducts = products.filter(function(product) { return product.rating >= 5 && product.price < 100; }); // Most declarative, implementation details are hidden in a function var bestProducts = GetBestProducts(); 

我喜欢剑桥课程的一个解释+他们的例子:

  • 声明式 – 指定要做什么而不是如何去做
    • 例如:HTML描述了网页上应该显示的内容,而不是应该如何在屏幕上绘制
  • 命令 – 指定什么如何
    • int x; – 什么(声明)
    • x=x+1; – 怎么样

在计算机科学中,声明式编程是一种expression计算逻辑而不描述其控制stream程的编程范式。

http://en.wikipedia.org/wiki/Declarative_programming

简而言之,声明性语言更简单,因为它缺乏控制stream(循环,if语句等)的复杂性。

ASP.Net的“代码隐藏”模式是一个很好的比较。 你有声明性的“.ASPX”文件,然后命令式的“ASPX.CS”代码文件。 我经常发现,如果我可以在脚本的声明性部分做所有我需要的事情,那么更多的人可以跟随正在做的事情。

命令式编程正在明确告诉计算机要做什么,以及如何做,如指定顺序等

C#:

 for (int i = 0; i < 10; i++) { System.Console.WriteLine("Hello World!"); } 

声明性是当你告诉计算机做什么,但不是真的如何去做。 Datalog / Prolog是在这方面想到的第一种语言。 基本上一切都是陈述式的。 你不能确保订单。

C#是更强制性的编程语言,但某些C#特性更像声明式的,就像Linq一样

 dynamic foo = from c in someCollection let x = someValue * 2 where c.SomeProperty < x select new {c.SomeProperty, c.OtherProperty}; 

同样的事情可以写成命令:

 dynamic foo = SomeCollection.Where ( c => c.SomeProperty < (SomeValue * 2) ) .Select ( c => new {c.SomeProperty, c.OtherProperty} ) 

(来自维基百科Linq的例子)

从这里偷菲利普·罗伯茨 :

  • 命令性的编程告诉机器如何做某事(导致你想要发生的事情)
  • 声明式编程告诉机器你想要发生什么(和计算机计算出如何做到这一点)

两个例子:

1.将数组中的所有数字加倍

势在必行:

 var numbers = [1,2,3,4,5] var doubled = [] for(var i = 0; i < numbers.length; i++) { var newNumber = numbers[i] * 2 doubled.push(newNumber) } console.log(doubled) //=> [2,4,6,8,10] 

声明的:

 var numbers = [1,2,3,4,5] var doubled = numbers.map(function(n) { return n * 2 }) console.log(doubled) //=> [2,4,6,8,10] 

2.将所有项目汇总在一个列表中

势在必行

 var numbers = [1,2,3,4,5] var total = 0 for(var i = 0; i < numbers.length; i++) { total += numbers[i] } console.log(total) //=> 15 

以声明

 var numbers = [1,2,3,4,5] var total = numbers.reduce(function(sum, n) { return sum + n }); console.log(total) //=> 15 

注意命令式的例子是如何创build一个新的variables,对它进行变异,并返回这个新的值(即如何使某事发生),而声明性的例子则是在一个给定的input上执行,并返回基于初始input的新值。 ,我们想要发生什么)。

差异主要与整体抽象层次有关。 用声明来说,在某些时候,你远离个别的步骤,程序在如何得到结果方面有很大的自由度。


你可以看看每一条指令落在一个连续的地方:

抽象度:

 Declarative <<=====|==================>> Imperative 

声明性的真实世界示例:

  1. 图书pipe理员,请检查一下我的白鲸的副本。 (图书pipe理员根据自己的判断select执行请求的最佳方法)

命令性的现实例子:

  1. 进入图书馆
  2. 查找图书组织系统(卡片目录 – 老学校)
  3. 研究如何使用卡片目录(你也忘了吧)
  4. 弄清楚如何标签和组织架子。
  5. 弄清楚架子上的书是如何组织的。
  6. 从卡片目录与组织系统交叉参考书籍位置find所述书籍。
  7. 拿书去检查系统。
  8. 看书。

只是为了增加移动应用程序开发的另一个例子。 在iOS和Android中,我们有Interface Builders,我们可以在其中定义应用程序的UI。

使用这些构build器绘制的UI本质上是声明式的,在这里我们拖放组件。 实际的行动发生在框架和系统之下。

但是我们也可以用代码来绘制整个组件,这在本质上是必要的。

另外,像Angular JS这样的一些新的语言正在专注于声明性地deviseUI,我们可能会看到许多其他语言提供相同的支持。 像JAVA没有任何良好的声明方式来绘制本地桌面应用程序在JAVA的swing或JAVA FX,但在不久的将来,他们可能会。

命令式编程
一种需要编程规范的编程语言,如C / C ++,Java,COBOL,FORTRAN,Perl和JavaScript。 编写这种语言的程序员必须根据数据处理和编程的知识,为解决这个问题制定适当的行动顺序。

声明式编程
一种不需要编写传统编程逻辑的计算机语言; 用户专注于定义input和输出,而不是程序编程语言(如C ++或Java)所需的程序步骤。

声明性编程示例是CSS,HTML,XML,XSLT,RegX。

声明性程序只是一些或多或less的“通用”命令性实现/虚拟机的数据。

加号:只是指定一些硬编码格式(和格式化)的数据,比直接指定一些命令式algorithm的变体更简单,更不容易出错。 一些复杂的规范不能直接编写,只能以某种DSLforms。 DSLs数据结构中使用的最佳和频率是集合和表格。 因为你没有元素/行之间的依赖关系。 当你没有依赖时,你可以自由地修改和缓解支持。 (比较模块与类 – 与模块,你很高兴和类你有脆弱的基类问题)声明性和DSL的所有货物立即从数据结构(表和集)的好处。 再加上 – 如果DSL是抽象的(精心devise的),你可以改变声明语言vm的实现。 例如,并行执行。 或将其移植到其他操作系统等所有良好的指定模块化隔离接口或协议给你这样的自由和便利的支持。

缺点:你猜对了。 通用(并通过DSL参数化)命令式algorithm/虚拟机执行可能比特定的更慢和/或内存饥饿。 在某些情况下。 如果这种情况是罕见的 – 只要忘了它,让它慢下来。 如果它是freciient – 你总是可以扩展你的DSL / VM的情况下。 某处放慢所有其他情况下,当然…

PS框架是DSL和命令之间的一半。 和所有的一切解决scheme…他们结合缺点,而不是利益。 他们不是那么安全,也不是那么快:)看看千斤顶的交易haskell – 这是强大的简单的ML和灵活的metaprog Prolog之间的中间和…它是多么怪物。 你可以把Prolog看作一个带有布尔函数/谓词的Haskell。 以及它对Haskell的灵活性有多简单

我只是想知道为什么没有人在C#中提到Attribute类作为声明性编程工具。 这个页面的stream行答案刚刚谈到了LINQ作为一种声明式编程工具。

根据维基百科

常见的声明性语言包括数据库查询语言(例如SQL,XQuery),正则expression式,逻辑编程,函数式编程和configurationpipe理系统。

所以LINQ作为一个函数语法,绝对是一个声明式的方法,但C#中的Attribute类作为一个configuration工具也是声明式的。 下面是阅读更多关于它的一个很好的起点: C#属性编程快速概述

从我的理解来看,这两个术语都有哲学的根源,有声明式和必要的知识。 陈述性知识是对真理的断言,像math公理这样的事实陈述。 它告诉你一些东西。 必要的或程序性的知识,一步一步地告诉你如何到达某物。 这就是algorithm本质上的定义。 如果你愿意的话,把计算机编程语言和英文比较一下。 陈述性句子陈述某事。 一个无聊的例子,但这里是一个声明的方式来显示两个数字是否相等,在Java中:

 public static void main(String[] args) { System.out.print("4 = 4."); } 

另一方面,英语中的强制性句子会发出命令或提出某种请求。 那么命令式编程只是一个命令列表(这样做,这样做)。 在Java中接受用户input时,显示两个数字是否相等,这是一个迫切的方法:

 private static Scanner input; public static void main(String[] args) { input = new Scanner(System.in); System.out.println(); System.out.print("Enter an integer value for x: "); int x = input.nextInt(); System.out.print("Enter an integer value for y: "); int y = input.nextInt(); System.out.println(); System.out.printf("%d == %d? %s\n", x, y, x == y); } 

从本质上讲,声明性知识跳过某些元素,形成一个抽象的层。 声明式编程也是一样的。