我一直在网上搜索声明式编程和命令式编程的定义,希望能给我一些启发。然而,我发现在一些资源中使用的语言令人生畏——例如在维基百科。 有没有人可以给我举一个现实世界的例子,让我对这个主题有一些了解(也许是c#)?


当前回答

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

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

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

一个很好的比较是ASP。网络“后台代码”模型。你们有陈述句。'ASPX '文件,然后是'ASPX. cs '代码文件。我经常发现,如果我可以在脚本的声明性部分完成我所需要的一切,那么更多的人可以理解所做的事情。

其他回答

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

pluses: specifying just a data, in some hardcoded (and checked) format, is simpler and less error-prone than specifying variant of some imperative algorithm directly. some complex specifications just cant be written directly, only in some DSL form. best and freq used in DSLs data structures is sets and tables. because you not have dependencies between elements/rows. and when you havent dependencies you have freedom to modify and ease of support. (compare for example modules with classes - with modules you happy and with classes you have fragile base class problem) all goods of declarativeness and DSL follows immediately from benefits of that data structures (tables and sets). another plus - you can change implementation of declarative language vm, if DSL is more-or-less abstract (well designed). make parallel implementation, for example. or port it to other os etc. all good specifed modular isolating interfaces or protocols gives you such freedom and easyness of support.

缺点: 你猜对了。一般的(通过DSL参数化的)命令式算法/虚拟机实现可能比特定的算法慢和/或占用内存。在某些情况下。 如果这种情况很罕见,那就忘了它吧,让它慢慢来。如果这种情况经常发生,你总是可以扩展你的DSL/vm。某个能减缓其他案件的地方,当然…

P.S.框架介于DSL和命令式之间。作为所有的折中方案……他们结合了缺点,而不是优点。它们不是那么安全,也不是那么快:)看看万事通haskell——它介于强大的简单ML和灵活的元程序Prolog之间……真是个怪物。你可以把Prolog看作一个Haskell,它只包含布尔函数/谓词。它对Haskell的灵活性是多么简单……

声明性编程是你说你想要什么,命令式编程是你说如何得到你想要的。

一个简单的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个核心进行快速并行搜索的最佳时机。作为程序员,你不需要考虑这些条件,也不需要编写代码来处理任何特殊情况。

我发现基于幂等性和交换性更容易区分陈述性和命令性。通过参考资料来了解他们。

看看这个简化版,了解幂等性。

然后引入“WHAT”和“HOW”的定义,以理解“WHAT”和“HOW”的实际含义。在声明式中,通过定义数据之间的关系来连接数据。你没有提到这种关系应该如何实现,而是“这种关系是什么”。通过关系来描述输出数据的“样子”,而不是“如何”来实现输出数据。

开始在脑海中画一些图表,画一些点(数据),用线(关系)连接起来。画所有可能的方式,一比多,多比一&一比一。为这些行添加箭头,如<-----------。所有箭头都应该朝左,因为必须先计算特定数据所基于的所有数据,然后再向左移动以计算该特定数据。

如果数据a基于数据b,那么数据c和数据d又可能基于其他一些数据。然后先计算b c d,然后才计算a。所以a在这条线的左边其他的都在右边。从b c d有3条线到达a。

这个图表有一些属性:

NO data will violate the relationship it has with all other data control flow or the order doesn't matter, of course b, c and d should be calculated before a but there is no preference between b, c and d i.e. it doesn't matter which one of these 3 is calculated first (commutative) a is only based upon b, c and d and no one else. Hence, it doesn't matter how many times the relationship operation that calculates a using b, c and d is executed, same a should be achieved (idempotent). a is the end result of the relationship operation here. Basically, everyone who is affecting a should have a line pointing to a.

这些关系(线)就像函数(数学函数而不是编程函数)。毫无疑问,函数式编程在学术界很有名。纯函数(在我们的编程中,因此不是粗体)就像函数(在数学中,因此是粗体)。

到目前为止,声明性可能听起来像PURE和IMMUTABLE(通常用于函数式编程),如果是GOOD,如果不是GREAT。因为这不是这里的目的,这是从这个模式中自动出现的。

如果你的一段代码可以转换成这个图表,那么它就是完全声明性的,否则,它就在尺度上的其他地方。

说明性很接近数学。

现在让我们放大这些关系(行),看看在程序执行期间计算机内部发生了什么。

命令式进来了。这就是底层工作完成的地方。在命令式中,你会一步一步地提到“如何”完成它,你知道这一系列步骤将在一个数据(输入b c d)和另一个数据(输出a)之间创建所请求的关系。在这里,你创建变量,改变它们,循环数组和所有其他事情。

命令式接近编程。

我不认为程序是声明式的或命令式的,我更喜欢把它放在最左边是完全声明式的,最右边是完全命令式的。记住,声明式是建立在命令式之上的,因此你看到的任何声明式的东西实际上都是命令式的。一般来说,程序是声明式和命令式的混合。

现在,让我们举两个例子:

第二个例子可以转换成这样的图表:

reduce_r map_r filter_r A <--------- b <--------- c <--------- d

Filter_r(关系):c只是d的偶数 Map_r(关系):b是所有数字乘以10的c Reduce_r (relationship): a是b加起来的所有数字

这应该看起来像数学的复合函数:reduce_r(map_r(filter_r(d)))

在声明中,开发人员的工作是将最终目标(a)分解为有助于实现最终目标的子目标(b, c)。

当然后台程序的映射、缩减和过滤是运行的必要代码。

值得思考的是:如果您需要对map函数做一个假设,从左到右,以使您的代码按预期工作,那么您实际上是在以声明的名义执行命令式操作。

参考资料:purpleidea (James), www.dataops.live, wiki.c2.com

这里和其他在线帖子的答案提到了以下内容:

使用声明式编程,您编写的代码描述您想要的东西,但不一定是如何得到它 您应该更喜欢声明式编程而不是命令式编程

他们没有告诉我们如何实现这一目标。为了使部分程序更具声明性,其他部分必须提供抽象来隐藏实现细节(即命令式代码)。

例如,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();

另外,声明式编程的极端是发明新的领域特定语言(DSL):

字符串搜索:正则表达式而不是自定义命令式代码 js: JSX而不是直接的DOM操作 AWS CloudFormation:用YAML代替CLI 关系型数据库:用SQL代替旧的读写api,如ISAM或VSAM。

声明式编程和命令式编程的一个很好的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);
}

这里,我们说:

创建一个结果集合 逐步检查集合中的每个数字 检查数字,如果是奇数,就把它加到结果中

另一方面,在声明式编程中,你写的代码描述了你想要的东西,但不一定是如何得到它(声明你想要的结果,但不是一步一步的):

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

这里,我们说的是“把所有奇怪的东西都给我们”,而不是“遍历集合”。选中此项,如果为奇数,则将其添加到结果集合中。”

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