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

时间:2009-11-23 17:24:42

标签: c# paradigms imperative-programming declarative-programming

我一直在网上搜索声明性和命令式编程的定义,这些定义可以为我提供一些启示。但是,我发现的一些资源中使用的语言令人生畏 - 例如在Wikipedia。 有没有人有一个现实世界的例子,他们可以告诉我这可能为这个主题带来一些看法(也许在C#中)?

17 个答案:

答案 0 :(得分:677)

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

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

例如,让我们从这个集合开始,然后选择奇数:

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. 创建结果集
  2. 逐步浏览集合中的每个数字
  3. 检查号码,如果是奇数,请将其添加到结果中
  4. 另一方面,使用声明性编程,您可以编写描述所需内容的代码,但不一定是如何获取代码(声明您想要的结果,但不是逐步说明) :

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

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

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

答案 1 :(得分:123)

声明性编程就是当你说你想要什么时,命令式语言是当你说如何来获得你想要的东西时。

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)

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

为了配合C#示例,通常使用LINQ会产生声明式样式,因为您并不是说如何来获取您想要的内容;你只是在说你想要什么。你可以对SQL说同样的话。

声明性编程的一个好处是它允许编译器做出可能导致代码比手工编写的代码更好的决策。运行SQL示例,如果您有像

这样的查询
SELECT score FROM games WHERE id < 100;

SQL“编译器”可以“优化”此查询,因为它知道id是索引字段 - 或者它可能没有编入索引,在这种情况下,它必须遍历整个数据集无论如何。或许SQL引擎知道这是利用所有8个内核进行快速并行搜索的最佳时机。 ,作为程序员,不关心任何这些条件,你不必编写代码来处理任何特殊情况。

答案 2 :(得分:92)

声明与命令

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

Declarative programming:是一种编程范式,表达了计算的逻辑(做什么)而没有描述其控制流(如何做)。 一些着名的声明性域特定语言(DSL)示例包括CSS,正则表达式和SQL子集(例如,SELECT查询) 许多标记语言(如HTML,MXML,XAML,XSLT ......)通常都是声明性的。 声明性编程试图模糊程序作为一组指令和程序之间的区别,作为关于所需答案的断言。

Imperative programming:是一种编程范例,它根据改变程序状态的语句来描述计算。声明性程序可以被视为编程命令或数学断言。

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

以下MSDN中的命令式编程示例循环显示数字1到10,并找到偶数。

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);

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

答案 3 :(得分:42)

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

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

在命令性结束时,您可以使用DirectX或OpenGL非常强制性地逐行绘制按钮,复选框等...(或者实际上,三角形三角形)。您可以自行决定如何绘制用户界面。

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

无论如何,还有另外一件事要考虑。仅仅因为一种语言是声明性的或命令式的,并不意味着它没有另一种语言的某些特征。

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

这一切的要点:

陈述 - &gt; what你想要完成

势在必行 - &gt; how你希望它完成

答案 4 :(得分:41)

以上所有答案和其他在线帖子都提到了以下内容:

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

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

  • 例如,LINQ比循环(for,while等)更具说明性,例如,您可以使用list.Where()来获取新的筛选列表。为了实现这一点,微软已经完成了LINQ抽象背后的所有繁重工作。

事实上,函数式编程和函数库更具说明性的原因之一是因为它们抽象了循环和列表创建,隐藏了场景背后的所有实现细节(很可能是带循环的命令式代码)。

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

最后,尽管函数式编程和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();

答案 5 :(得分:23)

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

  • 陈述性 - 指定要执行的操作不是来执行此操作
    • 例如:HTML描述了应该在网页上显示的内容,而不是如何在屏幕上显示
  • 势在必行 - 同时指定 如何
    • int x; - 什么(声明性的)
    • x=x+1; - 如何

答案 6 :(得分:22)

  

命令式编程要求开发人员逐步定义如何   应该执行代码。以强制性的方式发出指示,   你说,“去第一街,左转进入Main,开两个街区,   右转进入Maple,然后停在左边的第三个房子。“   声明版可能听起来像这样:“开车到苏的   房子。“有人说怎么做;另一个人说需要做什么   完成。

     

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

     
      
  • 它不会强迫旅行者记住一长串指示。
  •   
  • 它允许旅行者在可能的情况下优化路线。
  •   

Calvert,C Kulkarni,D(2009)。必不可少的LINQ。艾迪生韦斯利。 48。

答案 7 :(得分:16)

差异主要与整体抽象水平有关。使用声明,在某些时候,你离各个步骤太远了,程序在如何获得结果方面有很大的自由。

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

抽象程度:

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

陈述性真实世界示例:

  1. 图书管理员,请检查一下Moby Dick的副本。 (图书管理员,由他们自行决定选择执行请求的最佳方法)
  2. 势在必行的现实世界示例:

    1. 进入图书馆
    2. 查找图书组织系统(卡片目录 - 旧学校)
    3. 研究如何使用卡片目录(你也忘了,对吧)
    4. 弄清楚货架的标签和组织方式。
    5. 弄清楚书架在书架上的组织方式。
    6. 从卡片目录与组织系统交叉参考书籍位置以查找该书。
    7. 带书到退房系统。
    8. 查看书籍。

答案 8 :(得分:10)

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

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的例子)

答案 9 :(得分:8)

  

在计算机科学中,声明性编程是一种编程范式,它表达了计算的逻辑而没有描述其控制流程。

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

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

一个很好的比较是ASP.Net'代码隐藏'模型。你有声明的'.ASPX'文件,然后是命令式'ASPX.CS'代码文件。我经常发现,如果我可以在脚本的声明性半部分中完成所有我需要的工作,那么就会有很多人能够按照所做的那样做。

答案 10 :(得分:7)

Philip Roberts here偷窃:

  • 命令式编程告诉机器如何做某事(导致你想要发生的事情)
  • 声明性编程告诉机器您想要发生什么(计算机会弄清楚如何操作)

两个例子:

<强> 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

注意命令性示例如何涉及创建新变量,变更它并返回该新值(即,如何使某些事情发生),而声明性示例在给定输入上执行并返回基于初始值的新值输入(即我们想要发生的事情)。

答案 11 :(得分:4)

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

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

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

答案 12 :(得分:2)

声明性程序只是一些或多或少的数据&#34;通用&#34;命令式实施/ vm。

加号: 以某种硬编码(和检查)格式指定一个数据比直接指定一些命令算法的变体更简单且更不容易出错。一些复杂的规范只能直接编写,只能以某种DSL形式编写。 DSL和数据结构中使用的最佳和频率是集合和表格。因为你没有元素/行之间的依赖关系。当你没有依赖关系时,你可以自由修改和轻松支持。 (比较具有类的模块 - 使用您喜欢的模块以及具有脆弱基类问题的类) 声明性和DSL的所有商品都立即从数据结构(表和集)的好处中获得。 另一个好处 - 如果DSL或多或少是抽象的(设计良好),你可以改变声明性语言vm的实现。例如,进行并行实现。或将其移植到其他操作系统等所有良好的模块化隔离接口或协议为您提供了这种自由和简单的支持。

弊: 你猜对了。通用(并由DSL参数化)命令式算法/ vm实现可能比特定的更慢和/或内存饥饿。在某些情况下。 如果这种情况很少见 - 只要忘掉它,就让它变慢。如果它的频繁 - 你总是可以为这种情况扩展你的DSL / vm。在某个地方放慢所有其他情况,确定......

P.S。框架在DSL和命令之间是中途。并且作为所有中途解决方案......它们结合了缺点,而不是利益。它们不是那么安全而且不那么快:)看看万能的千斤顶 - 它在强大的简单ML和灵活的metaprog Prolog之间的中间位置......它是一个什么样的怪物。你可以将Prolog看作是一个只有布尔函数/谓词的Haskell。它的灵活性对Haskell有多么简单......

答案 13 :(得分:2)

我只是想知道为什么没有人提到属性类作为C#中的声明性编程工具。这个页面的流行答案刚刚讨论了LINQ作为声明性编程工具。

根据维基百科

  

通用声明性语言包括数据库查询语言   (例如,SQL,XQuery),正则表达式,逻辑编程,   函数式编程和配置管理系统。

因此LINQ作为一种功能语法绝对是一种声明性方法,但C#中的属性类作为配置工具也是声明性的。以下是阅读更多信息的好起点:Quick Overview of C# Attribute Programming

答案 14 :(得分:2)

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

使用这些构建器绘制的UI本质上是声明性的,我们拖放组件。实际的抽象发生在框架和系统之下,并由框架和系统执行。

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

此外,一些像Angular JS这样的新语言专注于以声明方式设计UI,我们可能会看到许多其他语言提供相同的支持。就像JAVA没有任何好的声明方式来在JAVA swing或JAVA FX中绘制原生桌面应用程序,但在不久的将来它们可能就是这样。

答案 15 :(得分:1)

根据我的理解,这两个词都源于哲学,有陈述性和命令式的知识。陈述性知识是真理的断言,是数学公理的事实陈述。它说 你有事。命令式或程序性知识会一步一步地告诉你如何达到目的。这就是算法的定义本质上是什么。如果愿意,可以将计算机编程语言与英语进行比较。陈述句陈述某事。一个无聊的例子,但这是一种在Java中显示两个数字是否相等的声明方式:

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

另一方面,英语中的命令句提供命令或提出某种要求。因此,命令式编程只是一个命令列表(这样做,这样做)。这是在Java中接受用户输入时显示两个数字是否相等的必要方法:

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);
}

基本上,声明性知识跳过某些元素,形成一个对这些元素的抽象层。声明式编程也是如此。

答案 16 :(得分:0)

命令式编程 - 您编写完成工作的代码

声明式编程 - 其他人编写完成工作的代码