当前位置:

首页 > 编程开发 > 声明式编程与命令式编程

声明式编程与命令式编程

先统一一下概念,我们有两种编程方式:命令式和声明式。 简单来说,两者的区别可以这样理解: 命令式编程:你告诉机器“怎么做” (how),至于你到底想要什么 (what),机器不管,它只负责执行你的指令。 声明式编程:你告诉机器“你想要什么” (what),然后让机器自己琢磨怎么去实现 (how)。

本文目录

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

    简单来说,两者的区别可以这样理解:

    • 命令式编程:你告诉机器“怎么做” (how),至于你到底想要什么 (what),机器不管,它只负责执行你的指令。
    • 声明式编程:你告诉机器“你想要什么” (what),然后让机器自己琢磨怎么去实现 (how)。

    光说概念可能有点绕,我们直接看代码。

    就拿一个最简单的例子来说:把数组里的每个数字翻倍。

    用命令式编程的思路,你会这么写:

    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]
    

    这段代码的逻辑很清楚:遍历整个数组,取出每个元素,乘以2,再把结果塞进一个新数组里。每一步都很具体,机器照做就行。

    那如果用声明式编程呢?可以用 Array.map 函数:

    var numbers = [1,2,3,4,5]
    var doubled = numbers.map(function(n) {
        return n * 2
    })
    console.log(doubled) //=> [2,4,6,8,10]
    

    map 函数厉害的地方在于,它把“遍历数组”这个底层操作给抽象出来了。我们只需要告诉它“我想要每个元素乘以2”这个结果,至于怎么遍历、怎么构建新数组,都是 map 内部的事情。注意,我们传给 map 的是一个纯函数——它只接收一个数字,然后返回乘以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
    

    而声明式方法,则用 reduce 函数:

    var numbers = [1,2,3,4,5]
    var total = numbers.reduce(function(sum, n) {
        return sum + n
    });
    console.log(total) //=> 15
    

    reduce 函数把一个列表“折叠”成一个值。它接收一个函数作为参数,这个函数会处理数组的每个元素。每次调用时,第一个参数(这里是 sum)是上一次处理的结果,第二个参数(n)就是当前元素。这样,每个新元素都会被累加到 sum 中,最终得到总和。同样,reduce 把如何遍历、如何管理状态的细节都封装好了,我们只需要告诉它“我想要什么”——把列表合并成一个值。

    第一次接触声明式编程,会不会觉得奇怪?

    如果你之前没接触过 map 和 reduce 这类函数,第一反应肯定是觉得奇怪。作为程序员,我们习惯了指定“如何做”。“遍历这个列表”、“如果这样,那就那样”、“把这个新值赋给这个变量”,这些都是我们熟悉的套路。我们明明已经知道怎么告诉机器去干活了,为什么还要去学这种看起来有点别扭的抽象工具?

    在很多场景下,命令式编程当然没问题。比如写业务逻辑时,我们经常需要写命令式代码,因为很多业务细节确实没法抽象出来。

    但是,一旦你花时间掌握了这些声明式的抽象工具,它们带来的好处是巨大的。首先,代码量明显减少,这是通往效率的捷径。更重要的是,它让你站在更高的层面去思考问题——你关注的是“想要什么”,而不是“怎么实现”。

    声明式编程的一个典型代表:SQL

    也许你还没意识到,其实你早就用过声明式编程了——那就是SQL。

    你可以把SQL看作一种专门处理数据的声明式查询语言。拿它来写一个完整的应用程序当然不现实,但如果是处理相互关联的数据集,它的威力就体现出来了。

    看这个查询:

    SELECT * from dogs
    INNER JOIN owners
    WHERE dogs.owner_id = owners.id
    

    如果用命令式编程来实现同样的逻辑,大概会是这样:

    // dogs = [{name: 'Fido', owner_id: 1}, {...}, ... ]
    // owners = [{id: 1, name: 'Bob'}, {...}, ...]
    var dogsWithOwners = []
    var dog, owner
    for(var di=0; di < dogs.length; di++) {
        dog = dogs[di]
        for(var oi=0; oi < owners.length; oi++) {
            owner = owners[oi]
            if (owner && dog.owner_id == owner.id) {
                dogsWithOwners.push({dog: dog, owner: owner})
            }
        }
    }
    

    这段代码可读性怎么样?如果你跟我一样,一开始接触SQL就觉得它挺难读的,别担心,这很正常。但关键在于,SQL代码不仅更短、更易读,它还有一个更大的优势:因为我们把“怎么做”的细节抽象掉了,就可以专注于“要什么”,然后把优化“怎么做”的任务交给数据库。那个命令式的双重循环显然会很慢,而SQL版本里,数据库可以自由选择使用索引、缓存或其他优化手段来快速找到我们想要的数据。我们不需要成为数据库专家,也能轻松完成任务。

    声明式编程的另一个生动案例:d3.js

    声明式编程的另一个威力巨大的领域是用户界面、图形和动画编程。

    开发用户界面是很复杂的事情。有用户交互,要创建漂亮的动态效果,往往会涉及大量的状态管理和重复代码,而这些代码其实是可以被抽象出来的。

    D3.js 就是一个很好的例子。它是一个利用Ja vaScript和SVG开发交互式、动画数据可视化工具库。如果你跟我一样,初学D3的时候觉得它很绕,这太正常了。但跟SQL一样,D3是一个强大的通用工具,它封装了所有“怎么做”的细节,让你只需要表达“想要什么”。

    来看个例子(这里推荐你实际去查看一下演示效果)。这是一个D3可视化程序,它为数据数组里的每个对象画一个圆。为了演示动态效果,程序每秒增加一个圆。

    其中最核心的代码是:

    // var data = [{x: 5, y: 10}, {x: 20, y: 5}]
    var circles = svg.selectAll('circle')
        .data(data)
    
    circles.enter().append('circle')
        .attr('cx', function(d) { return d.x })
        .attr('cy', function(d) { return d.y })
        .attr('r', 0)
        .transition().duration(500)
        .attr('r', 5)
    

    没必要完全理解这段代码的每一行(确实需要花点时间去领会)。关键点在于:

    首先,我们获取了SVG中所有现有的圆,然后把数据数组绑定到这些圆上。D3 内部维护了一个关系表,把每个圆和对应的数据点关联起来。初始时,我们有两个数据点,但没有圆(因为页面是空的)。这时,.enter() 方法就会为每个没有对应圆的数据点,创建一个新的圆。接着,我们声明这个新圆的圆心坐标(cx 和 cy),初始半径设为0,然后在半秒内通过动画过渡到半径为5。这就是一个典型的声明式描述:我们只关心最终的状态和变化过程,而D3负责处理所有底层的动画计算和DOM操作。

    命令式编程是对硬件操作的抽象,程序员通过指令精确地告诉计算机每一步该干什么。而声明式编程,则是函数式编程的一个重要特点。函数式编程还有其他特点,比如高阶函数、函数无副作用、只有值没有变量、用递归代替迭代等等。要完全掌握函数式编程,确实需要彻底刷新你的思维方式,甚至忘掉命令式的习惯,所以学习曲线比较陡峭。

    但这并不妨碍“声明式”这个特点在某些特定领域的应用——因为它确实能极大地简化代码,让你专注于真正重要的事情:你想要什么。

    本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系bd@zhengruan.com
    作者最新文章
    编程开发
    相关文章 更多
    GitHub Actions重跑失败作业会使用最新代码吗
    GitHub Actions重跑失败作业会使用最新代码吗

    解释已有运行重跑的提交和权限边界,给出确认、失败作业重跑及结果核对命令,区分漏触发事件。

    chat-demo v0.1.1怎么本地运行并理解DeepSeek历史数据边界
    chat-demo v0.1.1怎么本地运行并理解DeepSeek历史数据边界

    chat-demo v0.1.1可以作为静态聊天客户端在本地运行,但模型、搜索、文件和语音能力仍由DeepSeek API提供。源码运行环境为Node.js 22.12及以上、Git与pnpm。Demo版本号不等于应用API版本;这套应用API未单独标版本,默认聊天模型ID是deepseek-flash。

    GitHub Copilot CLI v1.0.111的用量图与上下文比例应该怎样理解
    GitHub Copilot CLI v1.0.111的用量图与上下文比例应该怎样理解

    理解GitHub Copilot CLI v1.0.111的高级请求百分比、用户提示活动图与实际路由模型窗口,区分显示口径和任务状态。

    n8n 2.47.6、2.47.7、2.48.3 Beta、2.48.4 Beta、3.2.2预发布与1.123.96适用哪些安全修复?
    n8n 2.47.6、2.47.7、2.48.3 Beta、2.48.4 Beta、3.2.2预发布与1.123.96适用哪些安全修复?

    按维护分支、利用权限与节点类型对应四项修复,区分正式补丁、Beta和临时缓解的适用边界。

    GitHub合并故障的已缓解、监控与已解决有什么不同
    GitHub合并故障的已缓解、监控与已解决有什么不同

    GitHub合并故障中的“已缓解并监控”和“已解决”不能互换。前者表示影响已经采取措施缓解,仍在观察稳定性;后者是供应商正式结束这场事故的状态。11月13日的Pull Requests合并超时事件先进入监控,随后于北京时间11月14日02:29标记为已解决。

    llama.cpp v1.0.0的网页聊天分组与Markdown编辑适合哪些场景
    llama.cpp v1.0.0的网页聊天分组与Markdown编辑适合哪些场景

    llama.cpp v1.0.0改善聊天组织、长消息编辑和附件预览;界面变化与底层API迁移、缓存及访问边界需要分别理解。

    Postman 12.36.4 更新后仍报错,怎么判断是哪一层的问题
    Postman 12.36.4 更新后仍报错,怎么判断是哪一层的问题

    Postman 12.36.4 更新后仍有异常时,可按应用、API请求和平台服务区分问题范围,避免把补丁版本当作故障原因。

    n8n 2.47.6、2.48.3 Beta与1.123.96适合哪些部署环境?
    n8n 2.47.6、2.48.3 Beta与1.123.96适合哪些部署环境?

    按正式、预发布和1.x维护渠道对应补丁,再结合Redis、Agent端点和Pinecone索引判断适用范围。

    Codex CLI 0.169.0与0.171.0-alpha.2怎样选择,终端工作区适合哪些任务
    Codex CLI 0.169.0与0.171.0-alpha.2怎样选择,终端工作区适合哪些任务

    Codex CLI 0.169.0提供有明确说明的终端工作区能力;0.171.0-alpha.2为预发布,当前没有功能清单,选用时应分开评估。

    llama.cpp b12345的Hexagon后端支持哪些Q5量化格式?
    llama.cpp b12345的Hexagon后端支持哪些Q5量化格式?

    llama.cpp b12345的预发布构建为Hexagon HTP后端增加Q5_0和Q5_1支持。选择模型时需要核对完整量化名称及实际运行后端,不能只看到“Q5”就视为同一种格式。

    查看更多
    精品专题 更多
    装机必备
    装机必备

    正软商城装机必备专区,精选办公、浏览器、安全防护、影音播放、压缩解压、设计创作和系统工具等电脑常用正版软件,帮助用户快速完成新电脑软件配置。

    Windows
    Windows

    正软商城Windows软件专区,汇集适用于Windows电脑的办公、设计、安全防护、影音播放、开发工具和系统优化软件,提供软件介绍、系统要求、正版授权及购买下载服务。

    PDF教程
    PDF教程

    正软商城PDF教程频道提供PDF编辑、转换、合并、拆分、压缩及格式处理方法,同时介绍常用PDF软件和工具的使用技巧。

    Mac软件 更多
    讯飞智作 网页版
    讯飞智作 网页版
    Windows/ macOS/ Linux

    讯飞智作是一款基于语音合成技术、虚拟人驱动技术与数字人形象声音构建能力的 AI 数字人音视频创作平台。

    火山引擎 网页版
    火山引擎 网页版
    Windows/ macOS/ Linux

    火山引擎是字节跳动旗下企业级云与AI服务平台。聚焦大模型核心能力,依托字节自研豆包多模态大模型,凭借火山方舟一站式平台,提供模型精调、推理部署、智能应用搭建等全链路服务,结合高性能AI算力,助力各行业快速落地大模型数字化与智能化应用。

    Aperty macOS版
    Aperty macOS版
    macOS

    Aperty 是 Skylum 推出的 AI 人像修图软件,支持自然肤质修饰、瑕疵处理、面部与身体调整、预设及批量同步。

    WINDOWS 更多
    讯飞智作 网页版
    讯飞智作 网页版
    Windows/ macOS/ Linux

    讯飞智作是一款基于语音合成技术、虚拟人驱动技术与数字人形象声音构建能力的 AI 数字人音视频创作平台。

    火山引擎 网页版
    火山引擎 网页版
    Windows/ macOS/ Linux

    火山引擎是字节跳动旗下企业级云与AI服务平台。聚焦大模型核心能力,依托字节自研豆包多模态大模型,凭借火山方舟一站式平台,提供模型精调、推理部署、智能应用搭建等全链路服务,结合高性能AI算力,助力各行业快速落地大模型数字化与智能化应用。

    PixTV 网页版
    PixTV 网页版
    Windows

    PixTV 是一款在浏览器中使用的 AI 视觉内容创作平台,提供图片、视频、音频、文字生成、AI 分镜、局部重绘、提示词反推和无限画布等工具,适合短剧、漫剧、广告及社交媒体内容制作。