Home Articles

功能性,声明式和命令式编程[关闭]

Asked
Viewed 1920 times
453

功能,声明和命令式编程这两个术语是什么意思?

14 Answers

  • 245

    在撰写本文时,此页面上的最高投票答案在声明性与命令性定义上是不精确和混淆的,包括引用维基百科的答案 . 一些答案以不同的方式混淆了这些术语 .

    另请参阅my explanation,为什么电子表格编程是声明性的,无论公式如何改变单元格 .

    此外,一些答案声称函数式编程必须是声明性的子集 . 在这一点上,它取决于我们将“功能”与“程序”区分开来 . 让我们首先处理命令式与声明性 .

    Definition of declarative expression

    可能将声明性表达式与命令式表达式区分开的 only 属性是其子表达式的引用透明度(RT) . 所有其他属性在两种类型的表达式之间共享,或者从RT派生 .

    100%声明性语言(即其中每个可能的表达式都是RT)不会(在其他RT要求中)允许存储值的突变,例如, HTML和大多数Haskell .

    Definition of RT expression

    RT通常被称为"no side-effects" . 术语效果没有精确的定义,因此有些人不同意"no side-effects"与RT相同 . RT有precise definition .

    由于每个子表达式在概念上都是函数调用,因此RT要求函数的实现(即被调用函数内的表达式)可能无法访问函数外部的可变状态(允许访问mutable local state) . 简而言之,功能(实现)应该是纯粹的 .

    Definition of pure function

    一个纯粹的功能通常被认为是"no side-effects" . 效果一词没有准确的定义,所以有些人不同意 .

    纯函数具有以下属性 .

    • 唯一可观察的输出是返回值 .

    • 唯一的输出依赖是参数 .
      在生成任何输出之前,

    • 参数已完全确定 .

    请记住,RT适用于表达式(包括函数调用),纯度适用于(函数的实现) .

    使用RT表达式的不纯函数的一个模糊示例是并发的,但这是因为在中断抽象层中纯度被破坏了 . 你真的不需要知道这一点 . 要制作RT表达式,可以调用纯函数 .

    Derivative attributes of RT

    用于声明性编程的任何其他属性,例如维基百科使用的citation from 1999既可以从RT派生,也可以与命令式编程共享 . 从而证明我的精确定义是正确的 .

    注意,external values的不变性是RT要求的一个子集 .

    • 声明性语言没有循环控制结构,例如 forwhile ,因为 due to immutability ,循环条件永远不会改变 .

    • 声明性语言不表示嵌套函数顺序(a.k.a逻辑依赖关系)以外的控制流,因为 due to immutability ,评估顺序的其他选择不会更改结果(参见下文) .

    • 声明性语言表示逻辑"steps"(即嵌套的RT函数调用顺序),但是每个函数调用是否是更高级别的语义(即"what to do")不是声明性编程的要求 . 与命令的区别在于(即更一般地为RT),这些"steps"不能依赖于可变状态,而是仅依赖于表达逻辑的关系顺序(即函数调用的嵌套顺序,即a.k.a.子表达式) .

    例如,在评估段落中的子表达式(即标记)之前,不能显示HTML段落 <p> . 由于标记层次结构的逻辑关系(子表达式的嵌套,它们是analogously nested function calls),因此没有可变状态,只有顺序依赖性 .

    • 因此存在不变性的衍生属性(更一般地说是RT),声明性表达式仅表达组成部分(即子表达式函数参数)的逻辑关系而不表示可变状态关系 .

    Evaluation order

    当任何函数调用不是RT(即函数不是纯粹的)时,子表达式的评估顺序的选择只能给出变化的结果,例如,在函数内访问函数外部的一些可变状态 .

    例如,给定一些嵌套表达式,例如 f( g(a, b), h(c, d) ) ,如果函数 fgh 是纯的,那么对函数参数的急切和懒惰评估将给出相同的结果 .

    然而,如果函数 fgh 不是纯粹的,那么评估顺序的选择可以给出不同的结果 .

    注意,嵌套表达式是概念上嵌套的函数,因为表达式运算符只是伪装成一元前缀,一元后缀或二进制中缀表示法的函数调用 .

    切线,如果所有标识符,例如, abc ,_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

    顺便说一下,Haskell有不同的语法, f (g a b) (h c d) .

    Evaluation order details

    函数是从输入到输出的状态转换(不是可变的存储值) . 对于调用纯函数的RT组合,这些状态转换的执行顺序是独立的 . 由于缺乏副作用和RT function may be replaced by its cached value原则,每个函数调用的状态转换独立于其他函数调用 . 至于correct a popular misconception,纯粹的monadic组成是 always declarative and RT ,尽管Haskell的 IO monad是arguably impure,因此势在必行w.r.t.程序外部的 World 状态(但在下面的警告意义上,副作用是孤立的) .

    急切评估意味着在调用函数之前评估函数参数,延迟评估意味着在函数内访问它们(以及它们) .

    Definition :函数参数在函数定义站点声明,函数调用在函数调用站点提供 . 知道参数和参数之间的区别 .

    从概念上讲,所有表达式都是(一个组合的)函数调用,例如常量是没有输入的函数,一元运算符是具有一个输入的函数,二进制中缀运算符是具有两个输入的函数,构造函数是函数,甚至控制语句(例如 ifforwhile )可以用函数建模 . 评估的order that these参数函数(不要与嵌套函数调用顺序混淆)不是由语法声明的,例如 f( g() ) 可以急切地评估 g 然后 fg 的结果,或者它可以评估 f 并且仅在 f 内需要其结果时懒惰地评估 g .

    警告,没有Turing complete语言(即允许无限递归)完全是声明性的,例如,懒惰评估引入记忆和时间不确定性 . 但是由于评估顺序的选择而产生的这些副作用仅限于内存消耗,执行时间,延迟,非终止和外部同步 .

    Functional programming

    因为声明性编程不能有循环,所以迭代的唯一方法是函数递归 . 从这个意义上讲,函数式编程与声明式编程有关 .

    functional programming is not limited to declarative programming . 功能组成可以是contrasted with subtyping,尤其是对于Expression Problem,其中扩展可以通过either adding subtypes or functional decomposition实现 . Extension can be a mix两种方法 .

    函数式编程通常使函数成为第一类对象,这意味着函数类型可以出现在任何其他类型的语法中 . 结果是函数可以输入和操作函数,从而通过强调函数组合来提供关注点分离,即分离确定性计算的子计算之间的依赖性 .

    例如,对于可以应用于集合的每个元素的无数个可能的专用动作中的每一个,函数编程采用可重复使用的迭代函数,例如,instead of writing a separate function(并且如果函数也必须是声明的,则使用递归而不是循环) . mapfoldfilter . 这些迭代函数输入一流的专用动作函数 . 这些迭代函数迭代集合并为每个元素调用输入专用操作函数 . 这些操作函数更简洁,因为它们不再需要包含循环语句来迭代集合 .

    但是,请注意,如果函数不纯,那么它实际上是一个过程 . 我们或许可以争辩说使用不纯函数的函数式编程实际上是程序式编程 . 因此,如果我们同意声明表达式是RT,那么我们可以说程序编程不是声明性编程,因此我们可能会认为函数式编程总是RT并且必须是声明性编程的子集 .

    Parallelism

    具有一等函数的这种功能组合可以通过分离出独立的功能来表达depth in the parallelism .

    布伦特原理:工作w和深度d的计算可以在时间O(max(w / p,d))的p处理器PRAM中实现 .

    并发性和并行性也是require declarative programming,即不变性和RT .

    那么这个危险的假设是Parallelism ==并发来自哪里呢?这是带有副作用的语言的自然结果:当你的语言到处都有副作用时,那么每次你尝试做多件事时你基本上都会产生由每个操作的效果交错引起的非确定性 . 因此,在副作用语言中,获得并行性的唯一方法是并发;因此,我们经常看到两者混为一谈并不奇怪 .

    FP评估订单

    请注意,评估顺序还会影响功能组合的终止和性能副作用 .

    渴望(CBV)和懒惰(CBN)是分类决斗[10],因为它们具有相反的评估顺序,即分别是首先评估外部函数还是内部函数 . 想象一个倒置的树,然后急切地从功能树分支提示分支层次结构到顶级功能主干;然而,懒惰评估从树干到分支提示 . 渴望没有分离的副产品("or",a / k / a分类"sums")[11] .

    Performance

    • 渴望

    与非终止一样,渴望过于渴望联合功能组合,即组合控制结构做了不必要的懒惰工作 . 对于example,当它由一个终止于第一个真元素的折叠组成时,急切地并且不必要地将整个列表映射到布尔值 .

    这种不必要的工作是在渴望与懒惰的连续时间复杂性中所声称的额外log n factor的原因,两者都具有纯粹的功能 . 一种解决方案是使用具有惰性构造函数的函子(例如列表)(即,渴望使用可选的惰性产品),因为急切地渴望不正确性源于内部函数 . 这是因为产品是建设性的类型,即在初始定点上具有初始代数的归纳类型[11]

    • 懒惰

    与非终止一样,懒惰过于懒惰,具有析取的功能组成,即共同结束可能发生在必要的晚期之后,导致不必要的工作和迟到的非确定性,而不是急切的[10] [11] . 最终的例子是状态,定时,非终止和运行时异常 . 这些是必要的副作用,但即使在纯粹的声明性语言(例如Haskell)中,在命令式IO monad中存在状态(注意:并非所有monad都是命令性的!)隐含在空间分配中,而时序是相对于命令式的状态真实世界 . 使用lazy甚至使用可选的热切副产品会泄漏"laziness"到内部副产品中,因为懒惰的懒惰不正确originates from the outer function(参见非终止部分中的示例,其中==是外部二元运算符函数) . 这是因为副产品受到终结性的限制,即在最终对象上具有最终代数的共同类型[11] .

    懒惰导致延迟和空间函数的设计和调试中的不确定性,其调试可能超出了大多数程序员的能力,因为dissonance between声明了函数层次结构和运行时评估顺序 . 使用eager评估的惰性纯函数可能会在运行时引入以前看不见的非终止 . 相反,使用lazy评估的热切纯函数可能会在运行时引入以前看不见的空间和延迟不确定性 .

    Non-termination

    在编译时,由于Halting问题和Turing完整语言中的相互递归,通常不能保证函数终止 .

    • 渴望

    对于 Head "and" Tail 的结合,如果 HeadTail 没有终止,则分别要么 List( Head(), Tail() ).tail == Tail()List( Head(), Tail() ).head == Head() 不为真,因为左侧不是,而右侧是,终止 .

    然而,懒惰双方终止 . 因此,渴望对联合产品过于渴望,并且在没有必要的情况下非终止(包括运行时异常) .

    • 懒惰

    对于 1 "or" 2 的分离,如果 1 "or" 2 没有终止,那么 List( f ? 1 : 2, 3 ).tail == (f ? List( 1, 3 ) : List( 2, 3 )).tail 不是真的,因为左侧终止,而右侧不终止 .

    然而,由于渴望双方都不会终止所以从未达到过平等测试 . 因此懒惰对于析取副产品来说太懒惰,并且在那些情况下,在做了比热切的工作更多的工作之后未能终止(包括运行时异常) .

    [10]陈述性延续和分类对偶性,Filinski,第2.5.4节CBV和CBN的比较,以及SCL中的3.6.1 CBV和CBN .

    [11]声明性延续和分类二元性,Filinski,第2.2.1节产品和副产品,2.2.2终端和初始对象,2.5.2具有惰性产品的CBV,以及具有急切副产品的2.5.3 CBN .

  • 97

    Imperative Programming 表示程序结构的任何编程风格 describing how the operations performed by a computer will happen .

    Declarative Programming 表示任何编程风格,其中您的程序是问题或解决方案的描述 - 但 doesn't explicitly state how the work will be done .

    Functional Programming 是通过评估函数的函数和函数进行编程...正如(严格定义的)函数式编程意味着通过定义无副作用的自由数学函数进行编程,因此 is a form of declarative programming but it isn't the only kind of declarative programming .

    Logic Programming (例如在Prolog中)是另一种形式的声明性编程 . 它涉及通过判断逻辑语句是否为真(或者是否可以满足)来进行计算 . 该程序通常是一系列事实和规则 - 即描述而不是一系列指令 .

    Term Rewriting (例如CASL)是另一种形式的声明性编程 . 它涉及代数术语的象征性转换 . 它完全不同于逻辑编程和函数式编程 .

  • 51

    简而言之:

    imperative language 指定计算机按顺序执行的一系列指令(执行此操作,然后执行此操作) .

    declarative language 声明了一组关于哪些输出应该来自哪些输入的规则(例如,如果你有A,那么结果是B) . 引擎会将这些规则应用于输入,并提供输出 .

    functional language 声明了一组数学/逻辑函数,用于定义输入如何转换为输出 . 例如 . f(y)= y * y . 它是一种声明性语言 .

  • 49

    imperative - 表达式描述要执行的操作序列(关联)

    declarative - 表达式是有助于程序行为的声明(关联,交换,幂等,单调)

    functional - 表达式只有效果值;语义支持等式推理

  • 22

    声明性编程是通过在输入和输出之间表达一些永恒逻辑来编程,例如,在伪代码中,以下示例将是声明性的:

    def factorial(n):
      if n < 2:
        return 1
      else:
        return factorial(n-1)
    
    output = factorial(argvec[0])
    

    我们在这里定义一个称为“阶乘”的关系,并将输出和输入之间的关系定义为该关系 . 这里应该很明显,关于任何结构化语言都允许在某种程度上进行声明性编程 . 声明性编程的一个中心思想是不可变数据,如果你分配给一个变量,你只会这样做一次,然后再也不会 . 其他更严格的定义意味着可能根本没有副作用,这些语言有时被称为“纯粹的声明性” .

    命令式风格的相同结果将是:

    a = 1
    b = argvec[0]
    while(b < 2):
      a * b--
    
    output = a
    

    在这个例子中,我们在输入和输出之间没有表达永恒的静态逻辑关系,我们手动更改了内存地址,直到其中一个保持了所需的结果 . 显而易见的是,所有语言在某种程度上都允许声明性语义,但并非所有语言都允许使用命令式,一些“纯粹”声明性语言允许副作用和完全变异 .

    声明性语言通常被指定为'what must be done',而不是'how to do it',我认为这是一个误称,声明性程序仍然指定必须如何从输入到输出,但另一方面,您指定的关系必须是有效可计算的(重要术语)如果你不知道,请查阅它 . 另一种方法是非确定性编程,它实际上只是指定了什么条件结果很多,在你的实现之前,只是在试验和错误之前耗尽所有路径,直到它成功 .

    纯粹的声明性语言包括Haskell和Pure Prolog . 从一个到另一个的滑动比例将是:Pure Prolog,Haskell,OCaml,Scheme / Lisp,Python,Javascript,C--,Perl,PHP,C,Pascall,C,Fortran,Assembly

  • 20

    简而言之,编程风格越强调什么(要做)抽象出如何(做到这一点)的细节越多,该风格被认为是声明性的 . 对于命令而言则恰恰相反 . 函数式编程与声明式样式相关联 .

  • 12

    自从我写完之前的答案以来,我已经制定了一个new definition的声明属性,引用如下 . 我还将命令式编程定义为双重属性 .

    这个定义优于我在之前的答案中提供的定义,因为它简洁而且更通用 . 但这可能更难以理解,因为适用于编程和生活的不完备性定理的含义对于人类来说难以包裹他们的思想 .

    引用的定义解释讨论了纯函数式编程在声明性编程中的作用 .

    所有奇异类型的编程都符合以下声明与命令的分类,因为以下定义声称它们是双重的 .

    声明性与命令性声明性属性是奇怪的,钝的,并且难以在技术上精确的定义中捕获,该定义仍然是一般的而不是模糊的,因为我们可以在不产生的情况下声明程序的含义(也称为语义)是一种天真的概念 . 意想不到的副作用 . 意义表达与避免意外效应之间存在着内在的张力,而这种紧张实际上源于编程和宇宙的不完备性定理 . 它过于简单化,技术上不精确,并且通常含糊不清地将声明定义为“做什么”,将命令称为“如何做” . 一个模糊的情况是“什么”是输出程序编译器的程序中的“如何” . 显然,使语言图灵完成的无界递归也类似于语义 - 不仅仅是在评估的句法结构中(也就是操作语义) . 这在逻辑上类似于哥德尔定理的一个例子 - “任何完整的公理系统也是不一致的” . 思考那句话的矛盾怪异!它也是一个演示如何表达的例子语义没有可证明的约束,因此我们无法证明一个程序(和它的语义类似)停止a.k.a. Halting定理 . 不完备性定理来源于我们宇宙的基本性质,正如热力学第二定律所述,“熵(a.k.a.独立可能性的数量)趋于最大化” . 程序的编码和设计永远不会完成 - 它还活着! - 因为它试图满足现实世界的需要,现实世界的语义总是在变化,趋向于更多的可能性 . 人类永远不会停止发现新事物(包括程序中的错误;-) . 为了在这个没有边缘的奇怪宇宙中精确地和技术上捕捉到上述理想的概念(思考宇宙中没有“外部”),需要一个简洁但看似不那么简单的定义,这个定义听起来不正确,直到它被解释为止 . 深 . 定义:声明性属性是只存在一组可以表达每个特定模块语义的可能语句的地方 . 命令性属性3是双重的,其中语义在组合下是不一致的和/或可以用语句集的变体来表达 . 声明性的这种定义在语义范围内具有明显的局部性,这意味着它要求模块化语义保持其一致的含义,无论在何处以及如何在全局范围内实例化和使用它 . 因此,每个声明性模块语义应该与所有可能的其他语义本质上正交 - 而不是不可能(由于不完整性定理)用于见证一致性的全局算法或模型,这也是Robert Harper教授的“更多并不总是更好”的观点 . 卡内基梅隆大学计算机科学系,标准ML的设计者之一 . 这些模块化声明性语义的示例包括类别理论仿函数,例如Applicative,名义类型,名称空间,命名字段和w.r.t.到语义的操作级别然后是纯函数式编程 . 因此,设计良好的声明性语言可以更清楚地表达意义,尽管在可以表达的内容中失去了一般性,但在内在一致性中可以表达的内容却有所增加 . 上述定义的一个例子是电子表格程序的单元格中的一组公式 - 当移动到不同的列和行单元时,预期不会给出相同的含义,即单元标识符改变 . 单元标识符是预期含义的一部分,而不是多余的 . 因此,每个电子表格结果都是唯一的w.r.t.到一组公式中的单元标识符 . 在这种情况下,一致的模块化语义是使用单元标识符作为单元格公式的纯函数的输入和输出(见下文) . 超文本标记语言a.k.a. HTML-静态网页的语言 - 是高度(但不是完美的)声明性语言的一个例子(至少在HTML 5之前)没有表达动态行为的能力 . HTML可能是最容易学习的语言 . 对于动态行为,JavaScript等命令式脚本语言通常与HTML结合使用 . 没有JavaScript的HTML符合声明性定义,因为每个名义类型(即标签)在语法规则内的组合下保持其一致的含义 . 声明性的竞争定义是语义语句的可交换和幂等属性,即语句可以在不改变含义的情况下重新排序和复制 . 例如,如果这些名称是模块化的,那么可以重新排序和复制为命名字段赋值的语句而不改变程序的含义 . 任何隐含的订单 . 名称有时意味着订单,例如单元格标识符包括它们的列和行位置 - 在电子表格上移动总计更改其含义 . 否则,这些属性隐式地要求语义的全局一致性 . 通常不可能设计语句的语义,因此如果随机排序或重复,它们保持一致,因为顺序和重复是语义固有的 . 例如,语句“Foo exists”(或构造)和“Foo不存在”(和销毁) . 如果考虑到预期语义的随机不一致性,那么人们接受这个定义对于声明性属性就足够了 . 从本质上讲,这个定义是空洞的,因为它会尝试使得与语义正交的一致性,即无视语义宇宙是动态无界的并且不能在全局一致性范例中捕获的事实 . 要求较低级操作语义的(结构评估顺序)的交换和幂等属性将操作语义转换为声明性本地化模块化语义,例如,纯函数式编程(包括递归而不是命令式循环) . 然后,实现细节的操作顺序不会影响(即全局扩展)更高级语义的一致性 . 例如,电子表格公式的评估顺序(理论上也是重复)无关紧要,因为在计算完所有输出之后,输出不会被复制到输入,即类似于纯函数 . C,Java,C,C#,PHP和JavaScript不是特别声明的 . Copute的语法和Python的语法更加声明地耦合到预期的结果,即一致的语法语义,消除了无关的内容,因此人们可以在忘记代码后轻松理解代码 . Copute和Haskell强制执行操作语义的确定性,并鼓励“不要重复自己”(DRY),因为它们只允许纯粹的功能范式 . 2即使我们可以证明程序的语义,例如使用语言Coq,这仅限于在键入中表达的语义,并且键入永远不能捕获程序的所有语义 - 即使对于不是图灵完整的语言也是如此,例如,使用HTML CSS,可以表达不一致的组合,因此具有未定义的语义 . 3许多解释错误地声称只有命令式编程具有语法上有序的语句 . 我澄清了命令式和函数式编程之间的这种混淆 . 例如,HTML语句的顺序不会降低其含义的一致性 .


    编辑:我将following comment发布到Robert Harper的博客:

    在函数式编程中...变量的变化范围是一种类型根据命令式编程与命令式编程的区别,命令式程序中的“可分配”也可能具有一种限制其可变性的类型 . 我目前对函数式编程感兴趣的唯一非混乱定义是:a)作为第一类对象和类型的函数,b)对循环递归的偏好,和/或c)纯函数 - 即那些不影响所需语义的函数当被记忆时程序(因此,由于操作语义的影响,例如存储器分配),在通用指称语义中不存在完全纯粹的函数编程 . 纯函数的幂等属性意味着对其变量的函数调用可以用其值替换,这通常不是命令式过程的参数的情况 . 纯函数似乎是声明性的w.r.t.到输入和结果类型之间的未组合状态转换 . 但纯函数的组合并不保持任何这样的一致性,因为可以用纯函数式编程语言来模拟副作用(全局状态)命令性过程,例如, Haskell的IOMonad,而且在任何图灵完整的纯函数式编程语言中完全不可能阻止这样做 . 正如我在2012年所写的那样,在你最近的博客中似乎有类似的评论共识,声明性编程试图捕捉到预期的语义从不透明的概念 . 不透明语义的例子是依赖于顺序,依赖于操作语义层的高级语义的擦除(例如,强制转换不是转换,而具体化的泛型限制了更高级别的语义),以及对无法检查的变量值的依赖(证明)正确的)编程语言 . 因此,我得出结论,只有非图灵完整语言才能声明 . 因此,声明性语言的一个明确且不同的属性可以是其输出可以被证明服从一些可枚举的生成规则集 . 例如,对于没有脚本化的任何特定HTML程序(忽略解释器发散方式的差异)(即不是图灵完成),则其输出可变性可以是可枚举的 . 或者更简洁的是,HTML程序是其可变性的纯函数 . 同上电子表格程序是它的纯粹功能输入变量 . 所以在我看来,声明性语言是无界递归的对立面,即根据哥德尔的第二个不完备性定理,自指示定理无法证明 . Lesie Lamport写了一篇童话故事,讲述了欧几里德如何通过类型和逻辑之间的一致性(Curry-Howard对应等)来解决在编程语言语境中应用于数学证明的哥德尔不完备性定理 .

  • 7

    我认为您的分类法不正确 . 命令式和声明性有两种相反的类型 . 功能只是声明的一个子类型 . 顺便说一句,维基百科说明了同样的事实 .

  • 5

    这里有一些关于注意到的“类型”的好答案 .

    我提交了一些通常与功能编程人群相关的其他更“异国情调”的概念:

    • Domain Specific LanguageDSL 编程:创建一种新语言来处理手头的问题 .

    • Meta-Programming :程序写入其他程序时 .

    • Evolutionary Programming :您构建一个不断改进自身或连续生成更好的子程序的系统 .

  • 3

    如今,新的焦点:我们需要旧的分类?

    过去的命令/声明/功能方面很好地对通用语言进行了分类,但是现在所有"big language"(如Java,Python,Javascript等)都有一些选项(通常是frameworks)用"other focus"来表达而不是它的主要语言(通常是frameworks)命令式),表达并行进程,声明性函数,lambdas等 .

    所以这个问题的一个很好的变体就是"What aspect is good to classify frameworks today?" ......一个重要的方面是我们可以标记"programming style" ...

    专注于数据与算法的融合

    一个很好的例子来解释 . 你可以read about jQuery at Wikipedia

    jQuery核心功能集 - DOM元素选择,遍历和操作 - 由其选择器引擎(...)启用,创建了一种新的“编程风格”,融合算法和DOM数据结构

    因此,jQuery是关注"new programming style"的最佳(流行)示例,不仅是面向对象,而是“面向面向对象的", is "面向DOM节点”......在此上下文中比较主要样式:

    • No fusion :在所有"big languages"中,在任何功能/声明/命令式表达式中,通常是"no fusion"的数据和算法,除了一些面向对象,这是strict algebric structure观点的融合 .

    • Some fusion :所有经典的融合策略,在今天都有一些框架使用它作为范例... dataflow,_ 1188092(或旧域特定语言为awkXSLT)...就像使用现代电子表格编程一样,它们也是 reactive programming 的例子样式 .

    • Big fusion :是"the jQuery style" ... jQuery是一种专注于“融合算法和DOM-data-structures”的领域专用语言 .
      PS:其他"query languages",作为XQuery,SQL(PL作为命令式表达式选项)也是数据算法融合示例,但它们是孤岛,没有与其他系统模块融合... Spring,当使用 find() -variants和Specification时条款,是另一个很好的融合例子 .

  • 2

    imperativedeclarative 描述了两种相反的编程风格 . 命令式是传统的"step by step recipe"方法,而声明性更为"this is what i want, now you work out how to do it" .

    这两种方法贯穿整个编程 - 即使使用相同的语言和相同的程序 . 通常,声明性方法被认为是优选的,因为它使程序员不必指定如此多的细节,同时也有更少的机会获得错误(如果你描述了你想要的结果,并且一些经过良好测试的自动过程可以从那个向后工作定义步骤然后你可能希望事情比必须手动指定每个步骤更可靠 .

    另一方面,一种命令式方法可以让您进行更低级别的控制 - 这是编程的“微管理方法” . 这可以让程序员利用有关问题的知识来提供更有效的答案 . 因此,程序的某些部分以更具说明性的方式编写并不罕见,但速度关键部分更为必要 .

    正如您可能想象的那样,您用来编写程序的语言会影响您的声明性 - 一种内置“智能”的语言,用于计算结果,给出对结果的描述将允许更多的声明程序员需要首先在命令式代码中添加这种智能才能在顶部构建更具声明性的层 . 因此,例如,像prolog这样的语言被认为是非常具有声明性的,因为它内置了一个搜索答案的过程 .

    到目前为止,你提到 functional 编程 . 's because it'这个词的含义与其他两个词没有直接关系 . 最简单的函数式编程意味着您可以使用函数 . 特别是,您使用支持函数的语言"first class values" - 这意味着您不仅可以编写函数,还可以编写函数来编写函数(编写函数......),并将函数传递给函数 . 简而言之 - 功能与字符串和数字一样灵活和通用 .

    然后,通常会一起提到功能性,命令性和声明性 . 其原因是将功能编程理念“推向极端” . 从最纯粹的意义上讲,函数是来自数学的东西 - 一种“黑盒子”,它接受一些输入并始终提供相同的输出 . 而且这种行为不需要存储变化的变量 . 因此,如果你设计一种编程语言,其目的是实现一种非常纯粹的,数学上受影响的函数式编程思想,那么你最终会主要拒绝可以改变的 Value 观(在一定的,有限的,技术意义上) .

    如果你这样做 - 如果你限制变量的变化 - 那么几乎是偶然的,你最终强迫程序员编写更具说明性的程序,因为命令式编程的很大一部分是描述变量如何变化,你不能再那样做了!事实证明,函数式编程 - 特别是函数式编程 - 往往会提供更多的声明性代码 .

    总结一下,然后:

    • 命令式和声明性是两种相反的编程风格(相同的名称用于鼓励这些风格的编程语言)

    • 函数式编程是一种编程风格,其中函数变得非常重要,因此,更改值变得不那么重要 . 指定值更改的能力有限会强制使用更具说明性的样式 .

    所以“函数式编程”通常被描述为“声明式” .

  • 2

    势在必行: how 实现我们的目标

    Take the next customer from a list.
       If the customer lives in Spain, show their details.
       If there are more customers in the list, go to the beginning
    

    声明: what 我们要实现

    Show customer details of every customer living in Spain
    
  • 2

    命令式编程:告诉“机器”如何做某事,结果你想要发生的事情就会发生 .

    声明性编程:告诉“机器”你想要发生什么,让计算机弄清楚如何去做 .

    势在必行的例子

    function makeWidget(options) {
        const element = document.createElement('div');
        element.style.backgroundColor = options.bgColor;
        element.style.width = options.width;
        element.style.height = options.height;
        element.textContent = options.txt;
    
        return element;
    }
    

    声明性示例

    function makeWidget(type, txt) {
        return new Element(type, txt);
    }
    

    注意:区别不在于简洁,复杂或抽象 . 如上所述,区别在于如何与什么相比 .

  • 1

    对于这些,没有任何非模棱两可的客观定义 . 我将如何定义它们:

    Imperative - 重点是计算机应采取的步骤而不是计算机将采取的步骤(例如C,C,Java) .

    Declarative - 重点是计算机应该做什么而不是应该如何做(例如SQL) .

    Functional - 一种重要关注递归的声明性语言的子集

Related