本文介绍了函数式,声明式和命令式编程的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

功能性,声明性和命令式编程是什么意思?

What do the terms functional, declarative, and imperative programming mean?

推荐答案

在撰写本文时,此页面上投票最多的答案在陈述式与命令式定义之间不准确,且糊涂,包括引述Wikipedia的答案.一些答案正在以不同的方式混淆这些术语.

At the time of writing this, the top voted answers on this page are imprecise and muddled on the declarative vs. imperative definition, including the answer that quotes Wikipedia. Some answers are conflating the terms in different ways.

还请参阅我对电子表格编程为什么是声明性的解释公式使细胞变异.

Refer also to my explanation of why spreadsheet programming is declarative, regardless that the formulas mutate the cells.

此外,有几个答案声称函数式编程必须是声明式的子集.在这一点上,这取决于我们是否将功能"与程序"区分开来.让我们先处理命令​​式与声明式.

Also, several answers claim that functional programming must be a subset of declarative. On that point it depends if we differentiate "function" from "procedure". Lets handle imperative vs. declarative first.

声明性表达式的定义

Definition of declarative expression

只有 only 属性可以将声明式表达式与命令式表达式区分开,这是其子表达式的参照透明(RT) .所有其他属性要么在两种类型的表达式之间共享,要么从RT派生.

The only attribute that can possibly differentiate a declarative expression from an imperative expression is the referential transparency (RT) of its sub-expressions. All other attributes are either shared between both types of expressions, or derived from the RT.

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

A 100% declarative language (i.e. one in which every possible expression is RT) does not (among other RT requirements) allow the mutation of stored values, e.g. HTML and most of Haskell.

RT表达式的定义

Definition of RT expression

RT通常被称为无副作用".术语效果没有确切的定义,因此有些人不同意无副作用"与RT相同. RT具有精确的定义.

RT is often referred to as having "no side-effects". The term effects does not have a precise definition, so some people don't agree that "no side-effects" is the same as RT. RT has a precise definition.

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

Since every sub-expression is conceptually a function call, RT requires that the implementation of a function (i.e. the expression(s) inside the called function) may not access the mutable state that is external to the function (accessing the mutable local state is allowed). Put simply, the function (implementation) should be pure.

纯功能

Definition of pure function

通常说一个纯函数没有副作用".效果 一词没有确切的定义,因此有些人不同意.

A pure function is often said to have "no side-effects". The term effects does not have a precise definition, so some people don't agree.

纯函数具有以下属性.

  • 唯一可观察到的输出是返回值.
  • 唯一的输出依赖项是参数.
  • 在生成任何输出之前已完全确定参数.

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

Remember that RT applies to expressions (which includes function calls) and purity applies to (implementations of) functions.

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

An obscure example of impure functions that make RT expressions is concurrency, but this is because the purity is broken at the interrupt abstraction layer. You don't really need to know this. To make RT expressions, you call pure functions.

RT的派生属性

为声明式编程引用的任何其他属性,例如Wikipedia使用的 1999年的引用,要么从RT派生出来,要么与命令式编程共享.因此证明我的精确定义是正确的.

Any other attribute cited for declarative programming, e.g. the citation from 1999 used by Wikipedia, either derives from RT, or is shared with imperative programming. Thus proving that my precise definition is correct.

请注意,外部值的不变性是RT要求的一部分.

Note, immutability of external values is a subset of the requirements for RT.

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

  • Declarative languages don't have looping control structures, e.g. for and while, because due to immutability, the loop condition would never change.

声明性语言除了嵌套函数顺序(也称为逻辑依赖项)外,不表达控制流,因为由于不变性,评估顺序的其他选择不会改变结果(请参见下面).

Declarative languages don't express control-flow other than nested function order (a.k.a logical dependencies), because due to immutability, other choices of evaluation order do not change the result (see below).

声明性语言表达逻辑步骤"(即嵌套的RT函数调用顺序),但是每个函数调用是否具有更高级别的语义(即做什么")并不是声明性编程的必要条件.与命令式的区别在于,由于不可变性(即更常见的是RT),这些步骤"不能依赖于可变状态,而只能依赖于所表达逻辑的关系顺序(即,嵌套逻辑的顺序).函数调用(又名子表达式).

Declarative languages express logical "steps" (i.e. the nested RT function call order), but whether each function call is a higher level semantic (i.e. "what to do") is not a requirement of declarative programming. The distinction from imperative is that due to immutability (i.e. more generally RT), these "steps" cannot depend on mutable state, rather only the relational order of the expressed logic (i.e. the order of nesting of the function calls, a.k.a. sub-expressions).

例如,HTML段落<p>不能显示,直到已经评估了该段落中的子表达式(即标签).由于标签层次结构的逻辑关系(没有子表达式的嵌套,它们是类似嵌套的函数调用).

For example, the HTML paragraph <p> cannot be displayed until the sub-expressions (i.e. tags) in the paragraph have been evaluated. There is no mutable state, only an order dependency due to the logical relationship of tag hierarchy (nesting of sub-expressions, which are analogously nested function calls).

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

Thus there is the derivative attribute of immutability (more generally RT), that declarative expressions, express only the logical relationships of the constituent parts (i.e. of the sub-expression function arguments) and not mutable state relationships.

评估顺序

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

The choice of evaluation order of sub-expressions can only give a varying result when any of the function calls are not RT (i.e. the function is not pure), e.g. some mutable state external to a function is accessed within the function.

例如,给定一些嵌套表达式,例如如果函数fgh是纯函数,则f( g(a, b), h(c, d) ),对函数参数的急切和惰性求值将得到相同的结果.

For example, given some nested expressions, e.g. f( g(a, b), h(c, d) ), eager and lazy evaluation of the function arguments will give the same results if the functions f, g, and h are pure.

相反,如果函数fgh不是纯函数,则选择评估顺序可以得出不同的结果.

Whereas, if the functions f, g, and h are not pure, then the choice of evaluation order can give a different result.

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

Note, nested expressions are conceptually nested functions, since expression operators are just function calls masquerading as unary prefix, unary postfix, or binary infix notation.

如果所有标识符(例如, abcd到处都是不可变的,无法访问程序外部的状态(即I/O),并且没有抽象层损坏,那么功能永远都是纯净的.

Tangentially, if all identifiers, e.g. a, b, c, d, are immutable everywhere, state external to the program cannot be accessed (i.e. I/O), and there is no abstraction layer breakage, then functions are always pure.

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

By the way, Haskell has a different syntax, f (g a b) (h c d).

评估订单详细信息

函数是从输入到输出的状态转换(不是可变的存储值).对于 pure 函数调用的RT组合,这些状态转换的执行顺序是独立的.由于没有副作用,并且 RT函数可以替换为它的原理,因此每个函数调用的状态转换都独立于其他函数调用缓存的值.要更正一个普遍的误解,纯单原子组成为 始终为声明性且RT ,尽管Haskell的IO monad是可以说是不纯的程序外部的World状态(但在下面的警告中,副作用是隔离的.)

A function is a state transition (not a mutable stored value) from the input to the output. For RT compositions of calls to pure functions, the order-of-execution of these state transitions is independent. The state transition of each function call is independent of the others, due to lack of side-effects and the principle that an RT function may be replaced by its cached value. To correct a popular misconception, pure monadic composition is always declarative and RT, in spite of the fact that Haskell's IO monad is arguably impure and thus imperative w.r.t. the World state external to the program (but in the sense of the caveat below, the side-effects are isolated).

更早的评估意味着在调用函数之前先评估函数参数,而懒惰的评估意味着参数不进行评估,直到(如果有)在函数中对其进行访问.

Eager evaluation means the functions arguments are evaluated before the function is called, and lazy evaluation means the arguments are not evaluated until (and if) they are accessed within the function.

定义:在函数 definition 位置声明函数 parameters ,并在函数 arguments 中提供函数功能 call 网站.了解参数参数之间的区别.

Definition: function parameters are declared at the function definition site, and function arguments are supplied at the function call site. Know the difference between parameter and argument.

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

Conceptually, all expressions are (a composition of) function calls, e.g. constants are functions without inputs, unary operators are functions with one input, binary infix operators are functions with two inputs, constructors are functions, and even control statements (e.g. if, for, while) can be modeled with functions. The order that these argument functions (do not confuse with nested function call order) are evaluated is not declared by the syntax, e.g. f( g() ) could eagerly evaluate g then f on g's result or it could evaluate f and only lazily evaluate g when its result is needed within f.

注意,没有完全转换语言(即允许无限制递归)完全是声明性的,例如惰性评估会引入内存和时间不确定性.但是由于选择评估顺序而产生的这些副作用仅限于内存消耗,执行时间,延迟,不终止以及,从而实现外部同步.

Caveat, no Turing complete language (i.e. that allows unbounded recursion) is perfectly declarative, e.g. lazy evaluation introduces memory and time indeterminism. But these side-effects due to the choice of evaluation order are limited to memory consumption, execution time, latency, non-termination, and external hysteresis thus external synchronization.

功能编程

由于声明式编程不能具有循环,因此唯一的迭代方法是函数递归.从这个意义上说,函数式编程与声明式编程有关.

Because declarative programming cannot have loops, then the only way to iterate is functional recursion. It is in this sense that functional programming is related to declarative programming.

但是功能性编程不限于声明性编程.可以与子类型进行对比,尤其是关于,其中扩展可以通过添加子类型或功能分解. 扩展可以是两种方法的组合.

But functional programming is not limited to declarative programming. Functional composition can be contrasted with subtyping, especially with respect to the Expression Problem, where extension can be achieved by either adding subtypes or functional decomposition. Extension can be a mix of both methodologies.

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

Functional programming usually makes the function a first-class object, meaning the function type can appear in the grammar anywhere any other type may. The upshot is that functions can input and operate on functions, thus providing for separation-of-concerns by emphasizing function composition, i.e. separating the dependencies among the subcomputations of a deterministic computation.

例如,而不是编写单独的函数(并采用递归而不是循环(如果函数也必须是声明性的),对于可能应用于集合的每个元素的无数个可能的专门动作中的每一个,函数式编程均采用可重用的迭代函数,例如mapfoldfilter.这些迭代函数输入一流的专门动作函数.这些迭代函数对集合进行迭代,并为每个元素调用输入专用动作函数.这些动作函数更加简洁,因为它们不再需要包含循环语句来迭代集合.

For example, instead of writing a separate function (and employing recursion instead of loops if the function must also be declarative) for each of an infinite number of possible specialized actions that could be applied to each element of a collection, functional programming employs reusable iteration functions, e.g. map, fold, filter. These iteration functions input a first-class specialized action function. These iteration functions iterate the collection and call the input specialized action function for each element. These action functions are more concise because they no longer need to contain the looping statements to iterate the collection.

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

However, note that if a function is not pure, then it is really a procedure. We can perhaps argue that functional programming that uses impure functions, is really procedural programming. Thus if we agree that declarative expressions are RT, then we can say that procedural programming is not declarative programming, and thus we might argue that functional programming is always RT and must be a subset of declarative programming.

平行主义

具有一流功能的这种功能组合可以表示

This functional composition with first-class functions can express the depth in the parallelism by separating out the independent function.

并发性和并行性也需要声明性编程,即不变性和RT.

Both concurrency and parallelism also require declarative programming, i.e. immutability and RT.

FP评估订单

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

FP evaluation order

Note the evaluation order also impacts the termination and performance side-effects of functional composition.

渴望(CBV)和懒惰(CBN)是绝对决斗[ 10 ],因为它们具有相反的求值顺序,即首先对外部函数还是内部函数求值.想象一下倒置的树,然后急切地从功能树分支求值,将分支层次结构推向顶层功能主干;而懒惰则从主干到分支提示进行评估.渴望的人没有合积(和",a/k/a类别的积"),懒惰的人没有合取的副产品(或",a/k/a类别的和")[ 11 ].

Eager (CBV) and lazy (CBN) are categorical duels[10], because they have reversed evaluation order, i.e. whether the outer or inner functions respectively are evaluated first. Imagine an upside-down tree, then eager evaluates from function tree branch tips up the branch hierarchy to the top-level function trunk; whereas, lazy evaluates from the trunk down to the branch tips. Eager doesn't have conjunctive products ("and", a/k/a categorical "products") and lazy doesn't have disjunctive coproducts ("or", a/k/a categorical "sums")[11].

性能

  • 渴望

  • Eager

与非终止一样,渴望过于渴望联合功能组成,即,组成控制结构会执行懒惰无法完成的不必要的工作.对于示例,他们会急切地,不必要地渴望当它由终止于第一个true元素的折叠组成时,会将整个列表映射为boolean.

As with non-termination, eager is too eager with conjunctive functional composition, i.e. compositional control structure does unnecessary work that isn't done with lazy. For example, eager eagerly and unnecessarily maps the entire list to booleans, when it is composed with a fold that terminates on the first true element.

这项不必要的工作是造成最多"一个额外的 log n因素的原因分别具有纯函数的渴望与懒惰的顺序时间复杂性.一种解决方案是将函子(例如列表)与惰性构造函数(即,渴望使用可选的惰性产品)一起使用,因为渴望的不正确性源自内部函数.这是因为乘积是构造性类型,即归纳类型,其初始代数在初始定点上[ 11 ]

This unnecessary work is the cause of the claimed "up to" an extra log n factor in the sequential time complexity of eager versus lazy, both with pure functions. A solution is to use functors (e.g. lists) with lazy constructors (i.e. eager with optional lazy products), because with eager the eagerness incorrectness originates from the inner function. This is because products are constructive types, i.e. inductive types with an initial algebra on an initial fixpoint[11]

懒惰

与非终止一样,懒惰对于分离的功能组成也过于懒惰,也就是说,共性最终性可能会比必要的发生晚,从而导致不必要的工作和对不确定性的不确定性,这不是渴望的[ 10 ] [ 11 ].最终状态的示例包括状态,时间,非终止和运行时异常.这些是命令性的副作用,但是即使使用纯声明性语言(例如Haskell),命令性IO monad中也存在状态隐含在空间分配中(注意:并非所有monads都是命令性的!),时序是相对于命令性而言的状态.真实世界.即使使用可选的急切的副产品,也使用惰性"会将惰性"泄漏到内部副产品中,因为对于惰性,惰性不正确源自外部函数(请参见非终止"部分的示例,其中==是外部二进制运算符函数).这是因为副产品受最终性约束,即在最终对象上具有最终代数的共归类型[ 11 ].

As with non-termination, lazy is too lazy with disjunctive functional composition, i.e. coinductive finality can occur later than necessary, resulting in both unnecessary work and non-determinism of the lateness that isn't the case with eager[10][11]. Examples of finality are state, timing, non-termination, and runtime exceptions. These are imperative side-effects, but even in a pure declarative language (e.g. Haskell), there is state in the imperative IO monad (note: not all monads are imperative!) implicit in space allocation, and timing is state relative to the imperative real world. Using lazy even with optional eager coproducts leaks "laziness" into inner coproducts, because with lazy the laziness incorrectness originates from the outer function (see the example in the Non-termination section, where == is an outer binary operator function). This is because coproducts are bounded by finality, i.e. coinductive types with a final algebra on an final object[11].

由于延迟已声明的函数层次结构与运行时评估顺序之间的不一致.渴望评估的惰性纯函数可能会在运行时引入以前看不见的非终止.相反,渴望用懒惰评估纯函数,可能会在运行时引入以前看不见的空间和延迟不确定性.

Lazy causes indeterminism in the design and debugging of functions for latency and space, the debugging of which is probably beyond the capabilities of the majority of programmers, because of the dissonance between the declared function hierarchy and the runtime order-of-evaluation. Lazy pure functions evaluated with eager, could potentially introduce previously unseen non-termination at runtime. Conversely, eager pure functions evaluated with lazy, could potentially introduce previously unseen space and latency indeterminism at runtime.

不终止

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

At compile-time, due to the Halting problem and mutual recursion in a Turing complete language, functions can't generally be guaranteed to terminate.

渴望而又不懒惰,对于Head和" Tail的结合,如果HeadTail都没有终止,则分别List( Head(), Tail() ).tail == Tail()List( Head(), Tail() ).head == Head()都不成立因为左侧不终止,右侧则终止.

With eager but not lazy, for the conjunction of Head "and" Tail, if either Head or Tail doesn't terminate, then respectively either List( Head(), Tail() ).tail == Tail() or List( Head(), Tail() ).head == Head() is not true because the left-side doesn't, and right-side does, terminate.

相反,懒惰的双方都终止了.因此,渴望使用联合产品,并且在不必要的情况下渴望不终止(包括运行时异常).

Whereas, with lazy both sides terminate. Thus eager is too eager with conjunctive products, and non-terminates (including runtime exceptions) in those cases where it isn't necessary.

懒惰

出于懒惰,但不急于将1或" 2分开,如果f没有终止,则List( f ? 1 : 2, 3 ).tail == (f ? List( 1, 3 ) : List( 2, 3 )).tail不成立,因为左侧终止了,右侧没有.

With lazy but not eager, for the disjunction of 1 "or" 2, if f doesn't terminate, then List( f ? 1 : 2, 3 ).tail == (f ? List( 1, 3 ) : List( 2, 3 )).tail is not true because the left-side terminates, and right-side doesn't.

鉴于此,双方都渴望终止,因此永远不会达到平等测试.因此,懒惰对析取的副产品过于懒惰,在那些情况下,完成比预期更多的工作后无法终止(包括运行时异常).

Whereas, with eager neither side terminates so the equality test is never reached. Thus lazy is too lazy with disjunctive coproducts, and in those cases fails to terminate (including runtime exceptions) after doing more work than eager would have.

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

[10] Declarative Continuations and Categorical Duality, Filinski, sections 2.5.4 A comparison of CBV and CBN, and 3.6.1 CBV and CBN in the SCL.

[ 11 ]声明性连续性和分类对偶,Filinski,第2.2.1节产品和副产品,2.2.2最终对象和初始对象,带有懒惰产品的2.5.2 CBV和带有渴望的副产品的2.5.3 CBN.

[11] Declarative Continuations and Categorical Duality, Filinski, sections 2.2.1 Products and coproducts, 2.2.2 Terminal and initial objects, 2.5.2 CBV with lazy products, and 2.5.3 CBN with eager coproducts.

这篇关于函数式,声明式和命令式编程的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

05-27 17:47
查看更多