陈述语言

时间:2012-08-30 02:43:58

标签: haskell imperative-programming declarative-programming imperative-languages

我正在阅读Declarative Programming Languages上的一篇文章。

如果我不了解这种类型/范式的编程语言的质量,并且它与命令式语言形成鲜明对比,那么我是否应该阅读这种编程语言的编程,如Haskell,以及然后稍后阅读那篇文章?

5 个答案:

答案 0 :(得分:8)

陈述范式的观点是懒惰。我们,声明性程序员,喜欢让编译器完成所有工作。尽我们所能,我们不会指定我们想要使用的算法,而只指定我们想要的结果的定义。

例如,如果在命令式设置中你想要计算直到n的整数之和,你可以写(在C中):

int f(int n) {
    int result = 0, i = 1;
    for(;i <= n; i ++)
        result += i;
    return result;
}

在声明性设置中,您只需声明此总和是什么(在Haskell中):

f 0 = 0
f n = n + f (n - 1)

这不是算法,它只是一个定义。但是Haskell编译器非常聪明,无论如何都能得到我们的结果。

当您切换到Prolog时更加可见,其中包含以下规范示例:

  1. 我们在不同的人之间宣布了一些关系:

    father(luke, anakin).
    father(anakin, a_slave_on_tattouin).
    father(a_slave_on_tattouin, someone).
    father(someone, adam).
    
  2. 然后我们说某人的祖先是他的父亲或他父亲的祖先:

    ancestor(Young, Old) :-
        father(Young, Old).
    
    ancestor(Young, VeryOld) :-
        father(Young, Old),
        ancestor(Old, VeryOld).
    
  3. 就是这样,Prolog可以释放魔法并回答如下问题:

    ?- ancestor(luke, X).
    X = anakin ;
    X = a_slave_on_tattouin ;
    X = someone ;
    X = adam ;
    false.
    

    有些人可以阅读上述内容:Head :- Body表示Head ,如果 Body。所以,上面,

    ancestor(Young, VeryOld) :-
        father(Young, Old),
        ancestor(Old, VeryOld).
    
    如果VeryOldYoung的父亲且OldYoung的祖先,则

    表示VeryOldOld的祖先。< / p>

    ;表示。所以Prolog告诉我们,luke有4个祖先,anakina_slave_on_tattouin等。

    正如你所看到的,我们没有详细说明算法,Prolog仍然可以做很棒的事情,例如向我们提供luke家谱的所有细节。这是声明式编程的强大功能:你指定数据,这是你关心的,其余的是处理编译器的聪明人的工作。

答案 1 :(得分:4)

可能需要一些时间来了解Haskell和Prolog以及其他任何事情,但这值得做。这是一个类比,让你开始声明与命令。

让我们想象你想知道当x是5.279时5sin(7x + 20)+ cos(x)是什么,而你所拥有的只是你的计算器。如果它是一个旧计算器,你必须告诉它如何做,以什么顺序。如果我们将MC用于清除内存为0而MR用于回忆内存,而M+用于将当前数字添加到内存中,则实际键入的是

MC       -- clear the memory
5.279    -- take the number 5.279
*7=      -- multiply it by seven
+20=     -- add twenty
sin=     -- find sin of that
*5=      -- times by five
M+       -- store that in the memory
5.279    -- take the number 5.279
cos      -- find cos of it
M+       -- add that to what you had in the memory
MR       -- get the total back out of memory onto the screen

你告诉计算器一步一步做什么计算,因为它没有被编程来理解数学,只是算术。通过将其转化为步骤,您计算的结构变得模糊不清。有些人喜欢这种循序渐进的方式,发现它非常简单,并且无法理解为什么其他人会因为其他方式而烦恼。有用。你不必相信计算器。

现在任何编程语言和任何现代计算器都可以让您输入

5*sin(7*5.279+20)+cos(5.279)

更清晰,过去十年购买的一些计算器甚至会让你做到

5.279 -> x
5sin(7x+20)+cos(x)

现代计算器让您根据自己的理解来表达问题,而不是将其分解为计算器的步骤。有些人喜欢这样做,只是说你是什么意思的做法,发现它非常简单,并且无法理解为什么其他人可能会被其他方式困扰。很明显。您无需向计算器解释。

虽然这个5.279 -> x; 5sin(7x+20)+cos(x)几乎就是你在命令式编程语言中解决这个特定问题的方式,但我们刚才谈到计算器的样式,以及第一种做法它与旧的计算器是很多更加迫切(一步一步)的风格,这与新的计算器是一个更加声明(只是说你的意思)风格

让我们改变背景,而不是使用计算器计算一个数字,我们使用一些毡技巧来绘制一个简单的无意义图片。我们假设在测量时我们总是从左上角开始。必要的(逐步)风格将是

take a piece of paper 7 by 10
pick up a red pen
put it at the point (3,4)   
draw a 2x4 rectangle here
colour it in
pick up a black pen
put it at the point (4,4)
draw a circle here radius 3
colour it in

这是声明性的,说你的意思是什么样的风格

on a piece of paper 7 by 10
red 2x4 rectangle at (3,4)
underneath
black circle radius 3 at (4,4)

这些不是命令式或声明式代码的示例,而是试图向您展示思维和编程风格的差异。在声明性编程中,您说出您的意思,然后使用必要的非常聪明的编译器将您的声明转换为最佳指令序列。因为你已经说过你的意思了它可以从根本上改变实现这一目标的代码,同时保留你的意思。你信任编译器。

在命令式编程中,您逐步说出您想要做的事情,确保自己您已经使用了您能想到的最有效的方法,然后编译器使用了几个消除不必要的缓慢的技巧,并转换为较低级别的指令序列。因为你已经指定了要做什么的命令,所以它可以做多少优化是有限的,但是你相信程序员已经做出了最好的选择。

(我已经谈到了信任,因为读过这个信任的草稿的人很重要 - 她解释说她使用计算器是必要的方式,因为如果她以声明的方式做到这一点,她就不会真的相信它。认为对于程序员来说是一样的。许多程序员不信任编译器足以声明 - 他们想要控件,并且喜欢自己优化代码。)

从这听起来,使用声明式样式会更容易,但是声明性语言通常会有一个陡峭的学习曲线,大多数人更喜欢命令式。传统上存在巨大的速度差异(命令性的开销较少,因为它更直接地类似于计算机的工作),但是一些更现代的声明性代码编译器似乎位于速度表的前几个中。很多时候 - 编译后的经文被解释比命令式和声明式有更大的区别,这就是为什么python等经常变慢,即使它们是必要的。

答案 2 :(得分:2)

要回答你的问题我应该阅读我建议答案是否定的,你应该开始编程。我真的不相信你可以很好地理解编程语言的实用性和适用性,或者它们的各种分类,而不用编写程序。阅读它们是对编程的补充,而不是替代它。

答案 3 :(得分:0)

通常被指出作为声明性编程示例的语言是Prolog(实际上仍然在我的#34;学习&#34;列表; SWI-Prolog可用{{3从Squeeze开始)。

当然,尝试它是最好的,但是如果您只是想要了解范式,那么可能只需要仔细研究一些in the Debian repos

答案 4 :(得分:-1)

声明性编程主要是关于发明特定于域的语言(DSL),然后在这些子语言中进行编程。对于那种特殊的风格,Haskell是一个非常好的选择。在Haskell中,几乎所有特定于域的语言都嵌入在宿主语言中。然后将它们称为嵌入式DSL(EDSL)。换句话说,您不需要编写解析器,但可以从Haskell的优雅轻量级语法中受益。我举几个例子。

假设您要对图像/视频处理进行编码。您注意到几乎所有过滤器都有一些共同点:它们根据周围环境更新点。为此特定目的,有一个名为 comonads 的抽象概念。您不表示状态更改,而是编码与过滤器关联的转换函数。换句话说,您编码数据相关性和依赖性。请注意模糊滤镜的令人难以置信的简洁性以及它适用于图片视频(任何Filterable)的事实:

blur :: (Filterable img) => Radius -> img -> img
blur radius = extend (average . surroundingPoints radius)

假设您要编码图形用户界面(GUI)。您会注意到几乎所有的GUI都有一些共同点:GUI的各个元素基本上是时变字段,它们之间具有相关性。对于该特定目的,存在称为功能反应式编程(FRP)的抽象概念。您不处理事件,而只是描述元素之间的数据关系:

text1 = textField "text1"
text2 = textField "text2"
sum   = fmap show (text1 + text2) <|> "Please enter numbers"

dialog =
    label "First number:" ~|~ text1
        ~---~
    label "Second number:" ~|~ text2
        ~---~
    label "Sum:" ~|~ label sum

构建对话框所需的一切都包含在本说明中,包括用户未输入数字时会发生的情况。这既不是特殊语言也不是伪代码。这是实际的Haskell代码(使用Netwire库)!