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

Bra*_*rad 494 c# paradigms imperative-programming declarative-programming

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

Ree*_*sey 733

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

通过命令式编程,您可以逐步告诉编译器您想要发生什么.

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

List<int> collection = new List<int> { 1, 2, 3, 4, 5 };
Run Code Online (Sandbox Code Playgroud)

通过命令式编程,我们将逐步完成,并决定我们想要什么:

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

在这里,我们说:

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

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

var results = collection.Where( num => num % 2 != 0);
Run Code Online (Sandbox Code Playgroud)

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

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

  • 在我看来,声明性编程只不过是一个抽象层. (212认同)
  • @PeteKirkham你使用的语法不是问题 - 声明性和命令性更多的是宣告你想要发生什么,而不是准确地解释*它是如何发生的.使用集成的语法或扩展方法语法是一个单独的问题. (26认同)
  • collection.Where使用声明性LINQ扩展方法.它不是使用C#语言功能,而是使用声明性API.我不想在这里混合消息,这就是为什么我避免在声明性方法之上构建语言的原因. (15认同)
  • +1.但是,你首先提到LINQ,但有什么例子呢? (8认同)
  • 这是一个很好的答案,但它正在回答不纯的功能和命令式编程之间的区别.`collection.Where`没有使用Linq提供的声明性语法 - 请参阅https://msdn.microsoft.com/en-us/library/bb397906.aspx示例,`来自集合中项目%2!= 0的项目item`将是声明形式.调用函数不会成为声明性编程,因为该函数位于System.Linq命名空间中. (8认同)
  • @ReedCopsey 阅读您的回答后,我有一种感觉,声明式编程是“只是信任并使用该功能,其他人已准备好供您使用”,对吗? (3认同)
  • @DrazenBjelovuk对于当前的计算机,这是非常正确的,因为从某种意义上来说,代码最终将变得势在必行。某些语言没有从声明式到命令式的内部转换,但可以直接编译为汇编,因此映射(声明式-&gt;命令式)仍然相同。确实存在“纯”声明性机器,并且有一天可能比命令式机器更高效。当前,它们污染严重,效率很低,并且开销很大,因为它们在确定工作的优先顺序上很差,因此,除了计算之外,它们还执行其他任务,例如在SO上写评论。 (3认同)
  • 很好的例子:https://tylermcginnis.com/imperative-vs-declarative-programming/ (3认同)
  • 引用 Martin Kleppmann 的《设计数据密集型应用程序》:“命令式语言指定用于确定结果的逐步算法”,而“声明性语言仅指定结果的模式”。 (2认同)
  • 但是编译器不会优化我们的代码吗?例如,除以 2 将转换为位移 1。所以我们并没有真正强迫编译器如何做这些小事情。这意味着,这个例子也是声明性的,只是规模很小。 (2认同)
  • 没错,@Ferazhu。正如上面所定义的,声明式编程和命令式编程之间的唯一真正区别在于您正在操作的抽象层。LINQ 是列表之上的抽象。列表也是数组之上的抽象。我认为真正的区别在于声明性代码描述了它的意图,而不存在正交问题。但是,其他地方必须有更多代码(相对命令式代码)来执行该意图。 (2认同)

Mar*_*off 136

声明式编程是当你说什么,你想要的,命令式语言是当你说怎么得到你想要的东西.

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)
Run Code Online (Sandbox Code Playgroud)

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

为了结合C#示例,通常使用LINQ会产生声明式样式,因为您没有说明如何获得所需内容; 你只说什么你想要的.你可以对SQL说同样的话.

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

SELECT score FROM games WHERE id < 100;
Run Code Online (Sandbox Code Playgroud)

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

  • Python示例不是声明性的. (26认同)
  • @Juanjo:它****是decalarative. (15认同)
  • 同意Juanjo和zenna - 循环结构在重构为较短的表示法时不会神奇地转换为声明性程序. (15认同)
  • 不同意@FelixFrank,并倾向于@ missingfaktor的"大胆"声明.执行此操作的传统"完全"声明方式是`filter(lambda x:x <5,range(20))`,只是对较短表示法的另一种重构.这与创建的列表理解表达式(具有清晰的"map"和"filter"部分)没有任何有意义的区别(参见[pep 202](https://www.python.org/dev/peps)/pep-0202 /))明确意图创建更简洁的表示法.在这种情况下,这种列表理解将更清晰/惯用. (11认同)
  • 这里的第一个声明如何比第二声明更具声明性? (2认同)
  • 在我看来,如果某些东西是声明性的,将取决于你编码的方式,而不是它是如何实际执行/编译的.如果你正在编写的代码描述了你想要的而不是如何实现它,那么就是声明性的.因此,即使调用是一个简单的构造,它仍然可以是声明性的.最后,所有内容都被编译成处理器指令,这总是必不可少的. (2认同)
  • 冒着犯遗传谬误的风险,我想说列表推导式是*显着的*声明性构造,它们来自 Haskell,这是一种纯粹的函数式编程语言,并且毫无疑问地属于声明性范例。事实上,引擎盖下有一个 for 循环和 `.append` 是不相关的。由于通用计算机体系结构的性质,您总是可以更深入地找到从根本上讲的命令式程序。 (2认同)
  • @FelixFrank 不同意。我认为这里存在一个根本性的误解。声明式编程只是一种*风格*。它是语法的属性,而不是语义的属性。“神奇地转换成循环”的“较短符号”*正是声明性程序*。 (2认同)

Ali*_*imi 95

声明与命令

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

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

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

函数式编程:是一种编程范式,将计算视为数学函数的评估,并避免状态和可变数据.它强调功能的应用,与强调状态变化的命令式编程风格形成对比.在纯函数语言(如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);
Run Code Online (Sandbox Code Playgroud)

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

  • 你能为面向对象添加描述吗? (7认同)
  • 很好的例子:https://tylermcginnis.com/imperative-vs-declarative-programming/ (2认同)

eng*_*rce 43

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

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

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

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

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

在任何程序中,您将始终拥有命令性和声明性代码,您应该的目标是隐藏抽象背后的所有命令代码,以便程序的其他部分可以以声明方式使用它们.

最后,尽管函数式编程和LINQ可以使您的程序更具说明性,但您可以通过提供更多抽象来使其更具声明性.例如:

// JavaScript example

// Least declarative
const bestProducts = [];
for(let i = 0; i < products.length; i++) {
    let product = products[i];
    if (product.rating >= 5 && product.price < 100) {
        bestProducts.push(product);
    }
}


// More declarative
const bestProducts = products.filter(function(product) {
    return product.rating >= 5 && product.price < 100;
});

// Most declarative, implementation details are hidden in a function
const bestProducts = getBestProducts();
Run Code Online (Sandbox Code Playgroud)

  • @GuillaumeF。它是关于创建特定于领域的抽象。例如,在银行业:您应该创建“debit”、“deposit”等函数,而不是重复类似的代码“account.balance += DepositAmount” (6认同)

Eri*_*bal 42

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

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

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

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

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

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

这一切的要点:

声明 - > what你想要完成

势在必行 - > how你想要它完成


ROM*_*eer 24

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

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

  • 不.它也可以被认为是**声明**因为你只是说**你想要什么** - *"使这个细胞边界变成蓝色"*.想象一下,您希望以命令式方法绘制相同的边框(例如:JavaScript).然后你需要说*"转到点(x1,y1),在这个点和(x2,y1)之间画一条蓝线,从(x2,y1)到(x2,y2)画一条蓝线,画一个蓝色从(x2,y2)到(x1,y2)的线,从(x1,y2)到(x1,y1)"*画一条蓝线. (7认同)

Gor*_*ulu 23

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

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

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

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


Luc*_*Fox 19

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


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

抽象程度:

Declarative <<=====|==================>> Imperative
Run Code Online (Sandbox Code Playgroud)

声明性真实世界示例:

  1. 图书管理员,请检查一下Moby Dick的副本.(图书馆员,由他们自行决定选择执行请求的最佳方法)

势在必行的现实世界示例:

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

  • 这有一定的道理,但这不是一个完整的定义.使用声明性编程,您可以说明最终目标,而不考虑起点.通过命令式编程,定义的起点很重要.这就像给出地址与给出指示的区别.无论你是谁,地址都很有用.如果您从其他地方开始,说明无效. (2认同)

McK*_*Kay 10

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

C#:

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

陈述是当你告诉计算机做什么,但不是真的如何做.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};
Run Code Online (Sandbox Code Playgroud)

必须写同样的事情:

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

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

  • 你有一个拼写错误:linq语句是声明性的,而不是不完美的(你有"C#特征更为必要,像Linq"应该是声明性的. (2认同)

ker*_*vin 8

在计算机科学中,声明性编程是一种编程范式,它在不描述其控制流的情况下表达计算的逻辑.

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

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

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


小智 7

Philip Roberts偷窃:

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

两个例子:

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]
Run Code Online (Sandbox Code Playgroud)

声明的:

var numbers = [1,2,3,4,5]

var doubled = numbers.map(function(n) {
  return n * 2
})
console.log(doubled) //=> [2,4,6,8,10]
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

以声明

var numbers = [1,2,3,4,5]

var total = numbers.reduce(function(sum, n) {
  return sum + n
});
console.log(total) //=> 15
Run Code Online (Sandbox Code Playgroud)

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

  • 对于这个问题的可怕答案,你的"声明式"编程的例子就是函数式编程的一个例子.'map'的语义是'按顺序将此函数应用于数组的元素'.您不会按运行顺序允许运行时有任何余地. (4认同)

kve*_*nda 6

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

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


Pre*_*raj 5

命令式编程
一种需要编程规则的编程语言,例如 C/C++、Java、COBOL、FORTRAN、Perl 和 JavaScript。使用此类语言编写的程序员必须根据数据处理和编程的知识制定正确的操作顺序以解决问题。

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

声明式编程示例包括 CSS、HTML、XML、XSLT、RegX。