
伪代码这东西几乎每个写程序的人都会用但很少见有人愿意为它定一套规范。我过去写伪代码也是随性至极想到哪儿写到哪儿一会儿用中文一会儿用英文循环有的写for、有的写foreach、有的干脆画箭头。直到有一次我拿三个月前写的一段伪代码去对接一个新需求结果愣是看了十分钟才明白自己当时想干什么。从那天起我开始认真整理一套个人版伪代码规范目前版本v0.1。这套规范不追求教科书式的严谨也不要求团队强制执行它的目标很明确让我自己三天后还能看懂让第一次看的人不用反复追问。如果你也经常写设计文档、算法草图、流程图或者需要在论文里放伪代码这篇内容应该能帮你少走不少弯路。1. 为什么我决定给自己定一套伪代码规范1.1 不是所有代码都需要完整写出来我们写方案时第一反应往往是把完整代码贴上去觉得这样够准确。但完整代码里有太多不属于当前讨论的东西编译配置、异常体系、日志输出、上下文切换这些细节很容易淹没核心思想。伪代码适合三种情况一是逻辑还没完全定下来需要快速尝试二是读者不一定懂你用的语言需要降低理解门槛三是想同时比较多个方案不想为每个方案都写一套能跑的实现。所以我给自己定的第一条规范不是怎么写而是先判断什么时候该写。比如我在设计一个重试策略时如果直接写Java代码大家会争论线程池大小和异常类型而不是重试条件本身。用伪代码写“当且仅当网络错误或超时最多重试三次间隔递增”就够了。个人版规范帮助我控制抽象粒度而不是一上来就落到完整实现。也就是说伪代码的“高光时刻”是方案还没完全确定、或者需要快速拉通多方认知的时候一旦逻辑已经确定并且要直接落地到某个语言就该切到真实代码。1.2 资料里的伪代码为什么别扭写论文、看教材、读开源博客的时候伪代码风格五花八门。有的像Pascal变量声明一大串开头还要Define一堆类型有的像Python完全靠缩进表达结构有的混合大量数学符号读起来像公式推理。这些风格不是不好而是服务于特定领域和期刊习惯直接拿来当模板写起来会觉得束手束脚。比如算法导论风格的伪代码用return、for each和下标适合学术表达但把它原样放进产品设计文档同事看完会问“那这个任务到底存到哪张表”个人版规范的核心是吸收这些资料的优点比如固定关键字、明确复杂度、用输入输出框定范围但不照搬它们的排版和格式。我们可以建立一个自己用着顺手、别人不觉得奇怪、又能放进多种场景的折中版本。个人版规范的价值就是把这些来源里的合理成分拆出来固定关键字、明确输入输出、复杂度标注这些都是可以借鉴的至于排版、符号、变量表则按自己的习惯重构。毕竟个人版服务的是“自己日常写和读”不是投稿。等到要投稿时再做格式转换成本远比一直用不舒服的模板低。1.3 伪代码规范的边界别变成第二种编程语言必须明确规范是约束不是枷锁。如果伪代码已经长成另一种语言要维护流、编译概念那还不如直接写真实语言。伪代码的生命力在于“比代码更接近自然语言比自然语言更接近结构”。因此个人版规范需要设边界例如不要求声明变量类型不处理并发线程细节不写import不关注函数重载控制结构关键字固定但逻辑表达式可以使用自然语言。这样既保留了表达能力又不会过度工程化。例如我在纸笔草图阶段写“for each task in tasks”完全不需要思考task是List还是数组更不需要考虑内存分配。伪代码一旦开始纠结这些就会变得比真实代码还难维护。个人版规范本质上是一份“克制说明书”它约束的是表达结构不是表达内容。结构稳定了内容才能自由流动。2. 个人版伪代码规范的核心约定2.1 三套括号的使用分工圆括号用于函数调用和条件表达式如 isValid(x)方括号用于索引和集合访问如 tasks[2]、map[key]花括号用于代码块或集合字面量如 {status: CLOSED}。个人版建议严格区分这样在一段混合表达式里看到方括号就知道是在取元素看到花括号就知道是一个结构体。有人觉得在纸上手写时括号太多可以改用中文描述但在文档里尽量统一。我的经验是伪代码中的括号使用不需要和真实语言完全一致比如不必纠结“数组索引从0还是1”在需要的地方旁注一句就行。另外同一篇文档里不要一会儿写 tasks[1] 表示第一个元素一会儿写 tasks[0]。保持“一种介质一种风格”能减少很多阅读时的心思转换。手写场景可以更松散但核心规则不变圆括号表示动作方括号表示取数花括号表示整体结构。2.2 控制结构的关键字固定写法固定使用一套关键字中英文选一套。我选择英文缩写风格if、else if、else、for each、while、switch、case、function、return、try、catch、throw。这样既不会太啰嗦又和主流代码相近。为什么不用“如果/那么/否则”如果读者全是中文语境用中文更亲切但一旦要放入论文或国际化协作英文关键字更通用。个人版建议“英文关键字 中文说明”混合关键字用英文注释和描述用中文。选择英文关键字还带来一个额外好处任何主流IDE或代码编辑器的关键字高亮都能直接识别。虽然伪代码通常不在IDE里写但如果你在Markdown编辑器的代码块里写高亮效果会很自然。示例if order.status ! PAID: return {success: false, reason: 订单状态异常}这样的写法既有代码的结构感又有中文注解的清晰度。固定之后写起来就不再犹豫。2.3 命名规则变量、函数、数据流变量用名词单个概念用词集合用复数布尔用is/has/can等前缀。函数用动词或动词短语比如fetchOrder、sendNotification如果中文伪代码就写“获取订单”。推荐在第一次出现时写明“表示什么”后面不重复解释。数据流的命名要体现来源和去向如inputText、parsedAddress、rawRecord。我见过很多人用data1、data2在伪代码里尤其难懂。虽然伪代码不需要类型但变量名还是应该体现业务含义。还有一个补充规则内部临时变量用短名字暴露给外部的名字写完整。临时变量像tmp、idx只活在当前伪代码片段内但函数名、参数名、返回值字段尽量写完整。这和真实代码的接口设计一个道理因为外部关心的接口远比内部实现细节更容易变化。2.4 算法步骤的编号与注释风格顶层步骤用数字1、2、3子步骤用1.1、1.2或a、b、c需要强调的边界条件用“注意”开头。注释统一用“//”因为方便与真实代码互相转写。注释只写“为什么”和“边界条件”不写“下面做什么”。例如“// 防止并发下单导致重复结算”是有效注释“// 这里进行判断”就是废话。如果注释只是复述代码删掉如果注释解释了为什么这样判断、什么情况下走另一条路留下来。伪代码本来就精简注释更应该承担“解释上下文”的作用。编号的价值在于评审会上可以直接说“看第2.3步”而不是“从入口开始往右数第三个圈”。3. 一套能直接套用的模板示例3.1 从订单超时自动关闭场景开始空讲规则很难用我们先看一个常见的业务场景订单超时自动关闭系统。需求是订单支付超时30分钟后自动关闭若用户正在操作则延后关闭时要发通知。用伪代码表达系统级主流程function runTimeoutScheduler(): while true: wait(1min) expiredOrders queryExpiredOrders(threshold30min) for each order in expiredOrders: if isUserOperating(order): postponeTimeout(order, extra10min) continue closeOrder(order) sendNotification(order, timeout_closed)你可能觉得这个例子太简单但简单正是伪代码的优势它可以用最少的规则表达清楚一套完整逻辑。这段伪代码里没有写数据库表结构没有写分布式锁细节但已经把核心逻辑和边界场景都说清了。读者能清楚看到“如果用户正在操作就延长10分钟”这就是伪代码的价值。3.2 带输入输出的函数级伪代码再看一个函数级示例解析用户填写的地址文本。完整实现可能涉及省份识别、正则、NLP模型但伪代码只需要呈现主干function parseAddress(rawText): cleanedText removeSpecialChars(rawText) province matchProvince(cleanedText) city matchCity(cleanedText, province) detail extractDetail(cleanedText, city) if not province or not city: return {ok: false, reason: 省市区识别失败} return {ok: true, address: {province, city, detail}}写这段代码时我刻意省略了“如何识别省份”的具体实现因为那是算法细节。伪代码关心的是“输入是什么、输出是什么、失败怎么处理”。这个例子里值得注意的点是返回值始终是一个结构体无论成功还是失败都有确定字段。这样的写法在伪代码阶段就约定了接口契约后续真实实现时API设计会少很多反复。我建议函数级伪代码都遵守“返回结构体”这个约定不要一会儿返回对象一会儿返回布尔值。3.3 多模块联动的流程级伪代码当涉及多个模块协作伪代码要表达时序和重试。例如一个异步任务调度器从任务拉取到结果回写个人版写法如下procedure mainFlow(): tasks fetchReadyTasks() for each task in tasks: result executeWithRetry(task, maxRetry3) if result.ok: writeResult(task, result.data) else: writeErrorLog(task, result.error) notifyAdmin(task, 三次重试仍失败)这里的executeWithRetry本身还可以再展开成伪代码但主流程里只需要把它当作一个黑盒。写流程级伪代码时要学会“分层抽象”主流程写清链路子流程用函数名代替必要时再单独展开。这里我特意只写了三层拉取、执行、回写。至于executeWithRetry内部怎么处理另外用一个函数展开这样看主流程的人不需要理解重试细节看细节的人可以直接定位到对应伪代码段落。4. 伪代码规范围绕不同场景的调整4.1 写论文和教材时怎么选层级论文里的伪代码有特殊要求算法编号、输入输出、复杂度、行号、数学符号。个人版规则在这里要做调整比如用更正式的“输入”“输出”变量名用数学斜体控制结构用粗体或等宽。最重要的是要在伪代码正下方给出复杂度分析否则审稿人可能追问。我的习惯是论文伪代码先按照个人版写一版然后套上LaTeX的algorithm环境再补复杂度注释。论文伪代码中的“输入”“输出”不一定是函数的入参出参也可能是算法的前置条件和后置条件。比如输入是“图G(V,E)”输出是“最短路径集合”。个人版规范在论文场景下要做角色切换从“代码草图语言”变成“算法描述语言”。这时关键字固定仍然有用但排版要更严谨变量名也要更贴近数学惯例。4.2 画流程图之前先用伪代码梳逻辑很多人画流程图直接上手画到一半发现判断分支交叉最后整张图像蜘蛛网。我建议先写伪代码再转成流程图。因为伪代码是线性的写完之后你自然知道有哪些分支、哪些循环画出来的流程图不会乱。比如上面订单超时的例子画流程图前伪代码里的continue和return已经告诉你需要几个判断框。我一般先画一个非常粗糙的流程轮廓再用伪代码填充细节最后画正式流程图。这个顺序很多人是反过来的先画图再写伪代码结果图上的分支和伪代码对不上。按照“伪代码驱动流程图”的方式至少能保证两边的结构一致。流程图规范也会反过来帮助伪代码规范化两者结合使用效率很高。4.3 做代码审查和设计文档时怎么用设计文档是伪代码最常见的落点。评审会议上如果贴完整代码讨论容易陷进“代码风格”“变量命名”等细节如果只用自然语言又很难确认逻辑正确。伪代码是两者之间的平衡。个人版规范在这里可以充当团队讨论的“通用语言”。你先按个人规范写提交评审时如果同事都认可这套个人规范就慢慢变成了团队规范。我在实际项目里就用这个方法把一个三页纸的重试设计文档压缩成半页伪代码会上讨论效率明显提高。代码审查时伪代码还能帮助审查者快速定位“这个分支是否覆盖了上游返回的错误码”“这段循环退出条件是否会被绕过”。比起在一大堆真实代码里找对应逻辑伪代码的抽象层级更适合讨论“设计是否合理”这个问题。5. 维护和演进个人规范也需要版本管理5.1 发现规则冲突时的处理方式个人规范也会遇到冲突。例如你一直写return但流程级伪代码里又经常想直接写“输出结果到文件”这时要不要写return我的处理方式是区分层级函数级伪代码保留return流程级伪代码用输出或写入这样的自然语言避免为了统一而扭曲表达。如果遇到更复杂的冲突比如“for each”和索引遍历哪种更常用建议做一次统计翻出自己最近写的十段伪代码看哪种写法占多数保留主流写法另一种只作为备注。这类冲突的决策原则是“哪个更接近自然表达就保留哪个”。伪代码需要保留自然语言成分不能为了形式上统一而牺牲表达的自然性。如果两种写法都经常用说明它们服务的场景不同可以放在不同章节分别说明。规范是服务写作习惯的不是用来绞杀写作习惯的。5.2 与其他规范并存时的取舍你所在团队或课程可能有自己的模板比如企业标准、竞赛论文模板。此时个人规范要让路不要强行保留个人风格。我的做法是准备两套版本一套是“个人速写版”用于草稿和日常笔记一套是“对外提交版”按照目标平台或团队的格式要求做转换。这两套的转换成本其实很低因为核心逻辑已经写清楚了。这里要摆正心态个人版规范不是一份“最高法”它更像私人工具箱里的自用扳手。用个人版顺手但到了明确要求使用某个公共模板的场合优先服从公共模板同时把个人风格中“让逻辑更清楚”的部分默默保留下来。真正有价值的是你习惯的思考顺序不是那套花哨的符号系统。5.3 给规范本身写说明书个人规范也应该有一页纸的说明书方便自己快速查询。我按如下表格整理元素推荐写法说明条件if / else if / else避免用问号表达式循环for each / while明确循环条件函数function name(args)入参和出参写清楚集合[]索引访问注释//只写为什么返回return函数级伪代码使用这是一张动态表不是一次性写完就定型。比如我一开始规定用“//”注释后来发现写论文时需要LaTeX注释就补充了一行“论文模式使用%”。个人规范允许“特例”但特例必须写进说明书否则下次又忘了。每次做完项目我都会回头更新一次大约只需要五分钟但能让下一代项目直接受益。6. 关于伪代码的常见误解和我的体验6.1 “伪代码不用规范反正临时用”这是最大的误解。正因为是临时用才更需要规范。人通常在压力下写草稿没有固定习惯时思路越乱写出来的越难回头用。反而是那些每天写伪代码的工程师几乎都有自己的一套惯性写法。个人规范本质上是一种“思维惯性”帮你把形式上的决策自动化。不需要在写的那一刻纠结“这里该用什么符号”把大脑带宽留给真正的问题。6.2 “伪代码贴近某种语言更好”我看过很多人写伪代码明明是Java程序员却硬要塞入强类型或者当前项目用Python伪代码也跟着写def self。这其实偏离了伪代码的初衷。伪代码要贴近的是“读者容易理解”而不是“某种语言长得像”。如果一个Python开发者看到你的伪代码像C他理解起来会更费劲。正确的思路是先想清楚读者是谁再决定抽象层级。如果读者是算法团队可以用更多数学符号如果读者是前后端同学尽量用产品或接口语言。6.3 “规范会拖慢写伪代码的速度”恰恰相反。当你把关键字、命名、注释风格都固定下来后写伪代码更像填空结构确定了只需要填写业务逻辑。真正拖慢速度的是反复犹豫这里要不要加类型这里是写数组还是写集合规范直接消灭这些犹豫。我刚开始适应这套规则的头几天确实会慢但一周之后就基本无感再往后写伪代码的速度比之前随性写还快因为不需要停下来做选择。6.4 这套规范对我的实际帮助我现在写设计文档的流程是先在空白文档里用伪代码把逻辑过一遍卡住的地方标记出来然后根据卡壳点决定是查资料、改方案还是补充设计最后才把伪代码翻译成完整代码或接口描述。整个过程里伪代码就像一个“逻辑净水器”帮我把表达层面的噪音过滤掉留下真正需要解决的问题。很多在完整代码里被语法掩盖的问题会在结构化伪代码中变得非常显眼比如缺失的边界条件、不合理的返回值、遗漏的异常路径。最后分享一个小技巧每次写完伪代码隔一天再读一遍把你觉得“卡壳”的地方圈出来。这些卡壳点往往就是设计缺陷或者表达模糊的地方修正它们之后再落真实代码返工率会低很多。如果你也想试别一步到位定一堆规则先挑三条最常用的约定比如固定关键字、固定命名风格、固定注释符号用一周试试。一周后你会发现伪代码规范带来的不只是文档整洁更是一种对待复杂问题的秩序感。