我一直在网上寻找声明式和命令式编程的定义,这将为我提供一些启发。但是,在我发现的某些资源中使用的语言令人生畏-例如在Wikipedia上。有没有人有一个真实的例子可以向我展示这个主题(也许是C#)的观点?
我一直在网上寻找声明式和命令式编程的定义,这将为我提供一些启发。但是,在我发现的某些资源中使用的语言令人生畏-例如在Wikipedia上。有没有人有一个真实的例子可以向我展示这个主题(也许是C#)的观点?
Answers:
LINQ是声明式与命令式编程的一个很好的C#示例。
使用命令式编程,您可以逐步告诉编译器您要发生的事情。
例如,让我们从这个集合开始,然后选择奇数:
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);
在这里,我们说的是“给我们所有奇怪的地方”,而不是“逐步检查集合。检查此项目,如果它很奇怪,请将其添加到结果集合中。”
在许多情况下,代码也将是两种设计的混合体,因此它并不总是黑白的。
collection.Where
不使用Linq提供的声明性语法- 有关示例,请参阅msdn.microsoft.com/zh-cn/library/bb397906.aspx,from item in collection where item%2 != 0 select item
这将是声明性形式。仅仅因为该函数在System.Linq命名空间中,调用函数就不会成为声明式编程。
声明式编程是当你说什么,你想要的,命令式语言是当你说怎么得到你想要的东西。
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个内核进行快速并行搜索的最佳时机。 你,作为一个程序员,不关心任何这些条件,你不必编写代码来处理这样的任何特殊情况。
filter(lambda x: x < 5, range(20))
,这只是将其重构为较短符号的另一种方式。这与列表理解表达式(具有清晰的“地图”和“过滤器”部分)没有任何有意义的区别,列表理解表达式的创建(请参阅pep 202)其明确意图是创建一个更简洁的符号。在这种情况下,这种列表理解将更加清晰/习惯。
声明式与命令式
一个编程范式是计算机编程的基本风格。主要有四个范例:命令式,声明式,功能式(被视为声明式范例的子集)和面向对象。
声明式编程:是一种编程范例,用于表达计算的逻辑(做什么)而没有描述其控制流程(如何做)。声明性领域特定语言(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);
这两个示例都得出相同的结果,并且一个不比另一个好或坏。第一个示例需要更多代码,但是代码是可测试的,命令式方法使您可以完全控制实现细节。在第二个示例中,代码可以说更具可读性。但是,LINQ无法让您控制幕后发生的事情。您必须相信LINQ将提供所请求的结果。
以上所有答案和其他在线帖子均提及以下内容:
他们还没有告诉我们如何实现这一目标。为了使程序的一部分更具说明性,其他部分必须提供抽象以隐藏实现细节(这是命令性代码)。
list.Where()
用来获取新的过滤列表。为此,Microsoft完成了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();
PS声明式编程的极限是发明新的领域特定语言(DSL):
debit
,deposit
等代替重复imparative码account.balance += depositAmount
我将添加另一个在声明式/命令式编程讨论中很少出现的示例:用户界面!
在C#中,您可以使用各种技术来构建UI。
在命令的最后,您可以使用DirectX或OpenGL非常命令地绘制按钮,复选框等...逐行(或者实际上是逐个三角形)。由您决定如何绘制用户界面。
在声明性的一端,您拥有WPF。您基本上编写了一些XML(是的,从技术上来说是“ XAML”),并且框架为您完成了工作。您说出用户界面的外观。由系统确定如何执行此操作。
无论如何,这是另一件事要考虑。仅仅因为一种语言是声明性或命令性语言并不意味着它不具有另一种语言的某些功能。
同样,声明式编程的一个好处是,通常通过阅读代码更容易理解目的,而命令式则使您可以更好地控制执行。
这一切的要旨:
声明式-> what
您想完成
势在必行-> how
您要完成它
我喜欢剑桥课程及其示例的解释:
int x;
-什么(说明性)x=x+1;
- 怎么样CSS
是势在必行呢?
差异主要与抽象的整体级别有关。使用声明式,在某些时候,您与各个步骤相距甚远,因此程序在如何获得结果方面具有很大的自由度。
您可以将每条指令视为连续的某个地方:
抽象度:
Declarative <<=====|==================>> Imperative
声明式真实世界示例:
命令式真实世界示例:
命令式编程要求开发人员逐步定义如何执行代码。要以命令式的方式发出指示,您可以说:“去第一街,左转到Main,行驶两个街区,右转到Maple,然后在左侧的第三座房屋停靠。” 声明性版本听起来可能像这样:“开车去苏的家。” 一个人说如何做某事;对方说需要做什么。
声明式风格比命令式风格有两个优点:
- 它不会强迫旅行者记住很长的说明。
- 它允许旅行者在可能的情况下优化路线。
Calvert,C库尔卡尼,D(2009)。基本LINQ。艾迪生·韦斯利(Addison Wesley)。48。
命令式编程明确地告诉计算机该做什么,以及如何做,例如指定顺序等。
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的示例)
在计算机科学中,声明性编程是一种编程范例,用于表达计算的逻辑而不描述其控制流程。
来自http://en.wikipedia.org/wiki/Declarative_programming
简而言之,声明性语言更简单,因为它缺乏控制流(循环,if语句等)的复杂性。
一个很好的比较是ASP.Net“代码隐藏”模型。您具有声明性的“ .ASPX”文件,然后具有命令性的“ ASPX.CS”代码文件。我经常发现,只要我能在脚本的声明性一半中做所有我想做的事情,就会有更多的人可以跟随所做的事情。
从菲利普·罗伯茨那里偷窃:
两个例子:
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
请注意,命令式示例如何涉及创建新变量,对其进行突变并返回该新值(即,如何使某件事发生),而声明性示例在给定输入上执行并根据初始输入返回新值(即, ,我们想要发生的事情)。
声明性程序只是其或多或少的“通用”命令式实现/ vm的数据。
优点:与直接指定某些命令性算法的变体相比,仅以某种硬编码(和检查)格式指定数据更简单且更不易出错。有些复杂的规范只能以某种DSL形式直接编写。DSL数据结构中使用的最佳和频率是集和表。因为您没有元素/行之间的依赖关系。并且当您没有依赖项时,您可以自由修改和简化支持。(例如,将模块与类进行比较-与您满意的模块以及与具有脆弱基类问题的类进行比较),声明性和DSL的所有优点都立即受益于该数据结构(表和集合)的好处。另一个优点-如果DSL或多或少是抽象的(设计良好),则可以更改声明性语言vm的实现。例如,进行并行执行。
缺点:你猜对了。通用(由DSL参数化)命令式算法/ vm实现可能比特定的算法慢和/或占用大量内存。在某些情况下。如果这种情况很少见-那就别管它了,让它变慢。如果频率很高-在这种情况下,您始终可以扩展DSL / vm。放慢所有其他情况的速度,确定...
PS框架介于DSL和命令式命令之间。作为所有中途解决方案……它们结合了缺陷而不是利益。他们不是那么安全,也不是那么快:)看一看所有行业的haskell-它介于强大的简单ML和灵活的metaprog Prolog之间,这是什么怪物。您可以将Prolog视为具有仅布尔函数/谓词的Haskell。以及针对Haskell的灵活性有多简单...
我只是想知道为什么没有人提到Attribute类作为C#中的声明性编程工具。该页面的热门回答刚刚谈到了LINQ作为声明性编程工具。
根据维基百科
常见的声明性语言包括数据库查询语言(例如SQL,XQuery),正则表达式,逻辑编程,功能性编程和配置管理系统的语言。
因此,LINQ作为一种功能语法,绝对是一种声明性方法,但是C#中的属性类(作为配置工具)也是声明性的。这是阅读更多内容的一个很好的起点:C#属性编程快速概述
只是在移动应用程序开发方面添加了另一个示例。在iOS和Android中,我们有Interface Builders,可以在其中定义应用程序的UI。
使用这些构建器绘制的UI本质上是声明性的,我们在其中拖放组件。实际绘图发生在框架的下方,并由框架和系统执行。
但是我们也可以在代码中绘制整个组件,这在本质上是必须的。
同样,一些新语言(例如Angular JS)也专注于声明式设计UI,我们可能会看到许多其他语言提供相同的支持。像Java一样,没有任何好的声明方式可以在Java swing或Java FX中绘制本机桌面应用程序,但是在不久的将来,它们可能会。
以我的理解,这两个术语都源于哲学,有陈述性和命令性的知识。陈述性知识是对真理的断言,是诸如数学公理之类的事实陈述。它告诉你一些事情。命令性或程序性知识逐步告诉您如何达成目标。这就是算法的本质。如果可以,请将计算机编程语言与英语进行比较。陈述性语句陈述了某些内容。一个无聊的示例,但这是一种用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);
}
本质上,声明性知识会跳过某些元素,从而在这些元素上形成抽象层。声明式编程也是如此。