首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >函数式,声明式和命令式编程?

函数式,声明式和命令式编程?

提问于 2017-12-19 03:43:52
回答 2关注 0查看 2.7K

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

回答 2

以往V

发布于 2017-12-19 04:08:18

编程语言主要有四种类型

声明式编程:专注于”做什么”而不是”如何去做”。在更高层面写代码,更关心的是目标,而不是底层算法实现的过程。

ex: css, 正则表达式,sql 语句,html, xml…

命令式编程(过程式编程) : 专注于”如何去做”,这样不管”做什么”,都会按照你的命令去做。解决某一问题的具体算法实现。

函数式编程:把运算过程尽量写成一系列嵌套的函数调用。

函数式编程强调没有”副作用”,意味着函数要保持独立,所有功能就是返回一个新的值,没有其他行为,尤其是不得修改外部变量的值。

所谓”副作用”(side effect),指的是函数内部与外部互动(最典型的情况,就是修改全局变量的值),产生运算以外的其他结果。(详细了解函数式编程请看阮一峰的函数编程初探文章)

举个简单的例子,了解三者区别

1.现有这样一个数学表达式

  • (1+2) * 3 / 4

命令式编程可能会这样写

  • var a = 1 + 2;
  • var b = a * 3;
  • var c = b / 4;

函数式编程如下写法

divide(multiply(add(1, 2), 3), 4)

函数式编程是声明式的一种类型,声明式强调目标而不是具体过程。

我们想让一个数组里的数值翻倍。

我们用命令式编程风格实现,像下面这样:

代码语言:txt
AI代码解释
复制
var numbers = [1,2,3,4,5]
代码语言:txt
AI代码解释
复制
var doubled = []
代码语言:txt
AI代码解释
复制
for(var i = 0; i < numbers.length; i++) {
代码语言:txt
AI代码解释
复制
  var newNumber = numbers[i] * 2
代码语言:txt
AI代码解释
复制
  doubled.push (newNumber)
代码语言:txt
AI代码解释
复制
}
代码语言:txt
AI代码解释
复制
console.log (doubled) //=> [2,4,6,8,10]

我们直接遍历整个数组,取出每个元素,乘以二,然后把翻倍后的值放入新数组,每次都要操作这个双倍数组,直到计算完所有元素。

而使用声明式编程方法,我们可以用 Array.map 函数,像下面这样:

代码语言:txt
AI代码解释
复制
var numbers = [1,2,3,4,5]
代码语言:txt
AI代码解释
复制
var doubled = numbers.map (function (n) {
代码语言:txt
AI代码解释
复制
  return n * 2
代码语言:txt
AI代码解释
复制
})
代码语言:txt
AI代码解释
复制
console.log (doubled) //=> [2,4,6,8,10]

map利用当前的数组创建了一个新数组,新数组里的每个元素都是经过了传入map的函数(这里是function (n) { return n*2 })的处理。

  map函数所做的事情是将直接遍历整个数组的过程归纳抽离出来,让我们专注于描述我们想要的是什么(what)。注意,我们传入map的是一个纯函数;它不具有任何副作用(不会改变外部状态),它只是接收一个数字,返回乘以二后的值。

心愿

发布于 2017-12-19 04:09:09

先统一一下概念,我们有两种编程方式:命令式和声明式。

  我们可以像下面这样定义它们之间的不同:

命令式编程:命令“机器”如何去做事情(how),这样不管你想要的是什么(what),它都会按照你的命令实现。

声明式编程:告诉“机器”你想要的是什么(what),让机器想出如何去做(how)。

  声明式编程和命令式编程的代码例子

  举个简单的例子,假设我们想让一个数组里的数值翻倍。

  我们用命令式编程风格实现,像下面这样:

代码语言:txt
AI代码解释
复制
var numbers = [1,2,3,4,5]
代码语言:txt
AI代码解释
复制
var doubled = []
代码语言:txt
AI代码解释
复制
for(var i = 0; i < numbers.length; i++) {
代码语言:txt
AI代码解释
复制
  var newNumber = numbers[i] * 2
代码语言:txt
AI代码解释
复制
  doubled.push (newNumber)
代码语言:txt
AI代码解释
复制
}
代码语言:txt
AI代码解释
复制
console.log (doubled) //=> [2,4,6,8,10]

  我们直接遍历整个数组,取出每个元素,乘以二,然后把翻倍后的值放入新数组,每次都要操作这个双倍数组,直到计算完所有元素。

  而使用声明式编程方法,我们可以用 Array.map 函数,像下面这样:

代码语言:txt
复制
代码语言:txt
AI代码解释
复制
var numbers = [1,2,3,4,5]
代码语言:txt
AI代码解释
复制
var doubled = numbers.map (function (n) {
代码语言:txt
AI代码解释
复制
  return n * 2
代码语言:txt
AI代码解释
复制
})
代码语言:txt
AI代码解释
复制
console.log (doubled) //=> [2,4,6,8,10]

  map利用当前的数组创建了一个新数组,新数组里的每个元素都是经过了传入map的函数(这里是function (n) { return n*2 })的处理。

  map函数所做的事情是将直接遍历整个数组的过程归纳抽离出来,让我们专注于描述我们想要的是什么(what)。注意,我们传入map的是一个纯函数;它不具有任何副作用(不会改变外部状态),它只是接收一个数字,返回乘以二后的值。

  在一些具有函数式编程特征的语言里,对于 list 数据类型的操作,还有一些其他常用的声明式的函数方法。例如,求一个list里所有值的和,命令式编程会这样做:

代码语言:txt
AI代码解释
复制
var numbers = [1,2,3,4,5]
代码语言:txt
AI代码解释
复制
var total = 0 for(var i = 0; i < numbers.length; i++) {
代码语言:txt
AI代码解释
复制
  total += numbers[i]
代码语言:txt
AI代码解释
复制
}
代码语言:txt
AI代码解释
复制
console.log (total) //=> 15

  而在声明式编程方式里,我们使用reduce函数:

代码语言:txt
AI代码解释
复制
var numbers = [1,2,3,4,5]
代码语言:txt
AI代码解释
复制
var total = numbers.reduce (function (sum, n) {
代码语言:txt
AI代码解释
复制
  return sum + n
代码语言:txt
AI代码解释
复制
});
代码语言:txt
AI代码解释
复制
console.log (total) //=> 15

  reduce函数利用传入的函数把一个list运算成一个值。它以这个函数为参数,数组里的每个元素都要经过它的处理。每一次调用,第一个参数(这里是sum)都是这个函数处理前一个值时返回的结果,而第二个参数(n)就是当前元素。这样下来,每此处理的新元素都会合计到sum中,最终我们得到的是整个数组的和。

  同样,reduce函数归纳抽离了我们如何遍历数组和状态管理部分的实现,提供给我们一个通用的方式来把一个list合并成一个值。我们需要做的只是指明我们想要的是什么?

  声明式编程很奇怪吗?

  如果你之前没有听说过map和reduce函数,你的第一感觉,我相信,就会是这样。作为程序员,我们非常习惯去指出事情应该如何运行。“去遍历这个list”,“if 这种情况 then 那样做”,“把这个新值赋给这个变量”。当我们已经知道了如何告诉机器该如何做事时,为什么我们需要去学习这种看起来有些怪异的归纳抽离出来的函数工具?

  在很多情况中,命令式编程很好用。当我们写业务逻辑,我们通常必须要写命令式代码,没有可能在我们的专项业务里也存在一个可以归纳抽离的实现。

  但是,如果我们花时间去学习(或发现)声明式的可以归纳抽离的部分,它们能为我们的编程带来巨大的便捷。首先,我可以少写代码,这就是通往成功的捷径。而且它们能让我们站在更高的层面是思考,站在云端思考我们想要的是什么,而不是站在泥里思考事情该如何去做。

  声明式编程语言:SQL

  也许你还不能明白,但有一个地方,你也许已经用到了声明式编程,那就是SQL。

  你可以把 SQL 当做一个处理数据的声明式查询语言。完全用SQL写一个应用程序?这不可能。但如果是处理相互关联的数据集,它就显的无比强大了。

  像下面这样的查询语句:

代码语言:txt
复制
代码语言:txt
AI代码解释
复制
SELECT * from dogs
代码语言:txt
AI代码解释
复制
INNER JOIN owners
代码语言:txt
AI代码解释
复制
WHERE dogs.owner_id = owners.id

  如果我们用命令式编程方式实现这段逻辑:

代码语言:txt
AI代码解释
复制
//dogs = [{name: 'Fido', owner_id: 1}, {...}, ... ]
代码语言:txt
AI代码解释
复制
//owners = [{id: 1, name: 'Bob'}, {...}, ...] var dogsWithOwners = []
代码语言:txt
AI代码解释
复制
var dog, owne
代码语言:txt
AI代码解释
复制
for(var di=0; di < dogs.length; di++) {
代码语言:txt
AI代码解释
复制
  dog = dogs[di]
代码语言:txt
AI代码解释
复制
  for(var oi=0; oi < owners.length; oi++) {
代码语言:txt
AI代码解释
复制
    owner = owners[oi]
代码语言:txt
AI代码解释
复制
    if (owner && dog.owner_id == owner.id) {
代码语言:txt
AI代码解释
复制
      dogsWithOwners.push ({
代码语言:txt
AI代码解释
复制
        dog: dog,
代码语言:txt
AI代码解释
复制
        owner: owne
代码语言:txt
AI代码解释
复制
      })
代码语言:txt
AI代码解释
复制
    }
代码语言:txt
AI代码解释
复制
  }}
代码语言:txt
AI代码解释
复制
}

  我可没说SQL是一种很容易懂的语言,也没说一眼就能把它们看明白,但基本上还是很整洁的。

  SQL代码不仅很短,不不仅容易读懂,它还有更大的优势。因为我们归纳抽离了how,我们就可以专注于what,让数据库来帮我们优化how。

  我们的命令式编程代码会运行的很慢,因为需要遍历所有list里的每个狗的主人。

  而SQL例子里我们可以让数据库来处理how,来替我们去找我们想要的数据。如果需要用到索引(假设我们建了索引),数据库知道如何使用索引,这样性能又有了大的提升。如果在此不久之前它执行过相同的查询,它也许会从缓存里立即找到。通过放手how,让机器来做这些有难度的事,我们不需要掌握数据库原理就能轻松的完成任务。

  声明式编程:d3.js

  另外一个能体现出声明式编程的真正强大之处地方是用户界面、图形、动画编程。

  开发用户界面是有难度的事。因为有用户交互,我们希望能创建漂亮的动态用户交互方式,通常我们会用到大量的状态声明和很多相同作用的代码,这些代码实际上是可以归纳提炼出来的。

  d3.js 里面一个非常好的声明时归纳提炼的例子就是它的一个工具包,能够帮助我们使用JavaScript和SVG来开发交互的和动画的数据可视化模型。

  第一次(或第5次,甚至第10 =次)你开发d3程序时可能会头大。跟SQL一样,d3是一种可视化数据操作的强大通用工具,它能提供你所有how方法,让你只需要说出你想要什么。

  下面是一个例子(我建议你看一下这个演示)。这是一个d3可视化实现,它为data数组里的每个对象画一个圆。为了演示这个过程,我们每秒增加一个圆。

  里面最有趣的一段代码是:

代码语言:txt
AI代码解释
复制
//var data = [{x: 5, y: 10}, {x: 20, y: 5}]
代码语言:txt
AI代码解释
复制
var circles = svg.selectAll('circle')
代码语言:txt
AI代码解释
复制
                    .data(data)
代码语言:txt
复制
代码语言:txt
AI代码解释
复制
circles.enter().append('circle')
代码语言:txt
AI代码解释
复制
           .attr('cx', function(d) { return d.x })
代码语言:txt
AI代码解释
复制
           .attr('cy', function(d) { return d.y })
代码语言:txt
AI代码解释
复制
           .attr('r', 0)
代码语言:txt
AI代码解释
复制
        .transition().duration(500)
代码语言:txt
AI代码解释
复制
          .attr('r', 5) 

  没有必要完全理解这段代码都干了什么(你需要一段时间去领会),但关键点是:

  首先我们收集了svg里所有的圆,然后把data数组数据绑定到对象里。

  D3对每个圆都绑定了那些点数据有一个关系表。最初我们只有两个点,没有圆,我们使用.enter()方法获取数据点。这里,我们的意图是画一个圆,中心是x和y,初始值是0 ,半秒后变换成半径为5。

  为什么我说这很有意思?

  从头再看一遍代码,想一想,我们是在声明我们想要的图案是什么样子,还是在说如何作图。你会发现这里根本没有关于how的代码。我们只是在一个相当高的层面描述我们想要的是什么:

我要画圆,圆心在 data 数据里,当增加新圆时,用动画表示半径的增加。

  这太神奇了,我们没有写任何循环,这里没有状态管理。画图操作通常是很难写,很麻烦,很让人讨厌,但这里,d3归纳提取了一些常用的操作,让我们专注于描述我们想要的是什么。

  现在再看,d3.js很容易理解吗?不是,它绝对需要你花一段时间去学习。而学习的过程基本上需要你放弃去指明如何做事的习惯,而去学会如何描述我想要的是什么。

  最初,这可能是很困难的事,但经过一些时间的学习后,一些神奇的事情发生了——你变得非常非常有效率了。通过归纳提取how,d3.js能让你真正的专注说明你想要看到的是什么,让你在一个个更高的层面解决问题,解放你的创作力。

  声明式编程的总结

  声明式编程让我们去描述我们想要的是什么,让底层的软件/计算机/等去解决如何去实现它们。

  在很多情况中,就像我们看到的一样,声明式编程能给我们的编程带来真正的提升,通过站在更高层面写代码,我们可以更多的专注于what,而这正是我们开发软件真正的目标。

  问题是,程序员习惯了去描述how,这让我们感觉很好很舒服——强力——能够控制事情的发生发展,不放走任何我们不能看见不能理解的处理过程。

  有时候这种紧盯着how不放的做法是没问题的。如果我需要对代码进行更高性能的优化,我需要对what进行更深一步的描述来指导how。有时候对于某个业务逻辑没有任何可以归纳提取的通用实现,我们只能写命令式编程代码。

  但大多数时候,我们可以、而且应该寻求声明式的写代码方式,如果没有发现现成的归纳提取好的实现,我们应该自己去创建。起初这会很难,必定的,但就像我们使用SQL和D3.js, 我们会长期从中获得巨大的回报!

和开发者交流更多问题细节吧,去 写回答
相关文章
JavaScript: 函数式编程 - 声明式函数
什么是声明式,我们将不再指示计算机如何工作,而是指我们明确希望得到的结果。这种编程方式会改变我们习以为常的命令式编程相比,会让我们的轻松许多。
西南_张家辉
2021/02/02
1.4K0
【基于JS 函数式编程 -1】什么是函数式编程 | 纯函数 | 命令式与声明式 | 优点
我们知道,在数学中,函数可以有如下形式: f(X) = Y ,即一个函数f ,以X作参数,返回输出结果Y。 据此,我们可以归纳一个函数:
前端修罗场
2023/10/07
3080
8、声明式和命令式
原生开发和Vue开发的模式和特点,我们会发现是完全不同的,这里其实涉及到两种不同的编程范式:
Qwe7
2022/06/30
6620
命令式到函数式编程
应用场景:当我们用到 if-elseif-else 的时候,可以考虑使用 Optional 语义。 举例说明:
lambeta
2018/08/17
4050
几段小代码解释Python命令式编程和函数式编程
所谓命令式编程,是指How to do,要通过指令告诉计算机如何一步一步地完成预定任务;而所谓函数式编程,可理解为What to do,只需要通过简单的指令告诉计算机要做什么就可以了,代码更加简洁、易理解、易维护和易并行。本文通过一个小问题来演示命令式编程和函数式编程在Python中的用法。 # 创建列表 lst = list(range(10)) print(lst) # 下面的代码使用不同方法创建新列表,新列表中的元素是lst列表中的对应元素加5 # 命令式编程,循环 result = [] for i
Python小屋屋主
2018/04/16
7020
跟着Kubernetes学设计—声明式or命令式
我们知道k8s是基于声明式设计的系统,我们只管告诉k8s我们想要达到的系统状态,至于怎么达到,是k8s自身需要处理的事情,比如下面的yaml:
tunsuy
2023/09/20
1650
跟着Kubernetes学设计—声明式or命令式
java函数式编程实例(函数式编程实例)
发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/128794.html原文链接:https://javaforall.cn
全栈程序员站长
2022/07/28
1.3K0
函数式编程
工作以来, 在编写程序的时候一直使用面向对象的思想. 当然, 对函数式编程也有所耳闻, 但也仅仅是有所耳闻, 从来没有上手写过.
烟草的香味
2021/04/28
1.1K0
函数式编程
函数式编程
函数式编程已经有比较长的历史了,如今的动态语言,很大程度上也受到了函数式编程(反过来名叫命令式编程)的启发。
四火
2022/07/15
6680
函数式编程
函数式编程
原文链接:https://note.noxussj.top/?source=cloudtencent 什么是函数式编程? 函数式编程(Functional Programming, FP)就是利用纯函
菜园前端
2023/05/30
4140
函数式编程
优点: 1、减少键盘的开销 2、便于理解 —— 例如:merge([1,2],[3,4]).sort().search("2") 3、方便单元测试 ——针对函数不涉及外部状态变化,参数固定返回结果也相同。 4、不用考虑死锁的问题 —— 不修改变量
OPice
2019/12/23
8100
Java 8 函数式编程Java 8 函数式编程
java.util.function.* @FunctionalInterface 都是函数接口,没有成员(状态)
林万程
2018/09/29
1.2K0
命令式和声明式,哪个才是你的菜
今天的主题是讨论一下“命令式”思想和“声明式”思想在分布式系统和微服务架构运维中的应用。 主要大纲 1. “命令式”和“声明式”的概念 2. 命令式思想在分布式系统和微服务架构中遇到的困境 3. 以Kubernetes的设计思想为例,介绍声明式思想的优势 4. 普元的实践 “命令式”和“声明式”的概念 “命令式”和“声明式”这两个概念最初来自于编程语言,这两个概念并不常见,所以我们首先将他们明确一下。 第一个是“命令式”: “命令式”有时也被称作“指令式”,好像有一
yuanyi928
2018/04/02
9.5K0
命令式和声明式,哪个才是你的菜
java函数式编程Function(java函数式编程实战)
JAVA版本最新的目前已经发布到11了,但目前市面上大多数公司依然在使用Java7之前版本的语法,然而这些编程模式已经渐渐的跟不上新时代的技术了。比如时下潮流前沿spring framework5中的响应式编程就是使用到了函数式编程的风格。
全栈程序员站长
2022/08/02
2.3K0
java函数式编程Function(java函数式编程实战)
声明式渲染与 data 函数
这是学习vue开发,必先了解的第一个特征。如前已经实现的App.vue组件,已经包括声明式渲染:
LIYI
2020/01/13
6420
声明式渲染与 data 函数
声明式编程Vue重新修炼
es6后对原来的var做了改正,为了保证块级作用域,我们强制要求必须使用let和const 变量--->let 常量--->const
名字是乱打的
2021/12/23
3800
声明式编程Vue重新修炼
RxJS 函数式与响应式编程
简单说,”函数式编程”是一种 “编程范式”(programming paradigm),也就是如何编写程序的方法论。
阿宝哥
2019/11/05
1.1K0
前端之变(六):引领式变革,从命令式UI到声明式UI
当我在2020年使用前端的技术栈去编写一个跨平台桌面App时,发现前端在UI方面其模式与我在移动端接触到的有很大的差异,那时候我意识到原来在前端,其UI使用的是另一种模式,后面我才知道它的名字:声明式UI
御剑
2021/07/20
4.6K0
Python|函数式编程|类的函数式实现
python作为一个现代语言,结合着OOP和FP的多种性质,不同于其他OOP语言,python中的函数具有自己的环境,让我们从这里入手,看看类在FP中可以如何实现.
朝闻君
2021/11/22
5690
进攻式和防御式编程
"进攻式编程",指的是一种编程策略,它与传统的防御性编程策略(程序员负责防止或处理可能发生的错误和异常)不同。
JusterZhu
2023/09/21
4520
进攻式和防御式编程

相似问题

声明式编程和命令式编程有什么区别?

2773

函数式编程中如何存在时间函数?

2450

网站是交互式还是非交互式?

0287

C#接口。隐式实现与显式实现?

2419

pg分布式?

074
相关问答用户
到家集团 | 技术VP擅长5个领域
某公司 | 程序员擅长1个领域
新浪微博 | 高级总监擅长4个领域
添加站长 进交流群

领取专属 10元无门槛券

AI混元助手 在线答疑

扫码加入开发者社群
关注 腾讯云开发者公众号

洞察 腾讯核心技术

剖析业界实践案例

扫码关注腾讯云开发者公众号
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档