不要写那么多注释!

如果用很多注释来“装饰”代码是件好事的话,那么在代码中加入大片大片的注释便是锦上添花了。是这样吗?事实上不完全是这样的。过犹不及,好心也会办坏事。

'*************************************************
' Name: CopyString
'
' Purpose: This routine copies a string from the source
' string (source) to the target string (target).
'
' Algorithm: It gets the length of "source" and then copies each
' character, one at a time, into "target". It uses
' the loop index as an array index into both "source"
' and "target" and increments the loop/array index
' after each character is copied.
'
' Inputs: input The string to be copied
'
' Outputs: output The string to receive the copy of "input"
'
' Interface Assumptions: None
'
' Modification History: None
'
' Author: Dwight K. Coder
' Date Created: 10/1/04
' Phone: (555) 222-2255
' SSN: 111-22-3333
' Eye Color: Green
' Maiden Name: None
' Blood Type: AB-
' Mother's Maiden Name: None
' Favorite Car: Pontiac Aztek
' Personalized License Plate: "Tek-ie"
'*************************************************

我常常不看开发者写的注释。似乎他们并不明白,其实他们的代码已经告诉了我它是怎样工作的;我们需要注释告诉我们的是,程序为什么这样工作。代码注释已被广泛地误解和滥用,以致于你都开始怀疑它们原本的价值——注释还有存在的必要吗?你在期望什么呢?要当心!这里有一段完全没有任何注释的代码:

r = n / 2;
while ( abs( r - (n/r) ) > t ) {
  r = 0.5 * ( r + (n/r) );
}
System.out.println( "r = " + r );

明白这段代码想要做什么吗?它看起来一清二楚,但到底是干什么用的呢?

让我们加上一行注释:

 // 用“牛顿-拉夫逊”近似法求解n的平方根
r = n / 2;
while ( abs( r - (n/r) ) > t ) {
  r = 0.5 * ( r + (n/r) );
}
System.out.println( "r = " + r );

这个应该就是我想要的了。不是吗?它介于完全没有任何注释与每隔一行就规规矩矩写上史诗般的注释这两种极端之间,看似一个不错的折中——你满意了吗?

未必!与其添加注释,我更愿意把这段代码重构成这样:

private double SquareRootApproximation(n) {
  r = n / 2;
  while ( abs( r - (n/r) ) > t ) {
    r = 0.5 * ( r + (n/r) );
  }
  return r;
}
System.out.println( "r = " + SquareRootApproximation(r) );


我一行注释也没有加,但这段神秘的代码现在已经非常容易理解了。 

尽管注释本身说不上好或者坏,它们却常常被用作支撑代码的“拐杖”。你应该总是专注于编写代码,而忘了还有注释这种东西的存在。这会迫使你竭尽全力使用最简单、最直白、最能自我说明的方式把代码写出来。

为了让你的程序员同伴们更易于阅读和理解你的代码,如果你已经重写、重构、甚至重新设计了很多遍——当你已经一筹莫展,已经想不出任何办法可以让你的代码变得更加浅显易懂——这时候,也只有在这时候,你才应该迫不得已地加上些注释来解释你的代码是做什么的。

就像Steve Yegge指出的那样,这是初级开发者与高级开发者之间的一个关键差别:

坦率地说,要是在以前,让我一下子看太多的代码会让我崩溃,它的复杂度已经超越了我能接受的极限。而当我不得不在那些代码的基础上继续工作的时候,我通常会把它们重写,或者至少也会写上大量的注释。现如今,当我碰到这种情况的时候我会披荆斩棘快速通过,而不会(过多地)抱怨。当我在脑子里有了一个明确的目标、并且有一段复杂的代码要写时,我会把时间花在代码实现上面,而不是(用注释)写下它的故事、讲给我自己听。

初级开发者依靠注释来讲故事,而实际上他们应该依靠代码本身。注释是“旁白”,它们有其自身的价值,但绝不能用来替代(故事里的)情节、人物和场景。

或许这才是关于代码注释不可告人的小秘密:想要写出好的注释,你必须是一位优秀的作家。注释跟代码不一样,它不是给编译器看的。注释是用来和其他人类交流想法的文字。尽管我跟(大多数)程序员同伴们关系很好,但我必须承认,与其他人有效沟通真的不是我们的强项。我曾经收到过我们小组里的一位程序员发来的电子邮件,里面只有三段,但着实把我搞得晕头转向。我们能相信这样的人会在代码里写出清晰、易懂的注释吗?于是我想,或许我们中的一些人去做那些我们擅长的事情会更好些——那就是,为编译器写代码,并且尽可能写得清楚些,而只把注释当作是最后没有办法的办法。

写出好的、有意义的注释是很难的。就像写代码一样,它是一门艺术;或许还要更加艺术一点!就像Sammy Larbi在“Common Excuses Used To Comment Code and What To Do About Them”(注释代码的常见借口以及应对方法)一文中所说的那样,如果你觉得你的代码在没有注释的情况下显得过于复杂、很难被人理解,那只能说明你的代码写得很糟糕。重写你的代码吧,直到它不再需要任何注释。如果经过了这些努力,你仍然觉得注释是必需的,那么就尽你所能加上注释吧。切记,小心!

更多 1
6
0
查看评论
11楼  西瓜一片 1小时前发表 [回复]
注释用来解释“所以然”。其它作用:分块标记,代码定位标记,在IDE文档里嵌入有对比的颜色--更加美观和护眼。
10楼  西瓜一片 2小时前发表 [回复]
注释有时候真不能少,比如有人为了某个特定需求,往代码里贴了块狗皮膏药,就那么一段,如果没有注释,看的人可能就晕了,会为了一个次要的分支浪费很多时间。如果有一句友好的提示,说明何时何地为了什么临时目的而加,那后来人就能跳过这一段,紧扣逻辑主线。
9楼  popopening 2小时前发表 [回复]
人家母语是英语,咱们是中文,没有中文注释我会愿意把你代码都看了才怪
8楼  initphp 2小时前发表 [回复]
扯蛋的文章
show me your code!
7楼  lymastee 2小时前发表 [回复]
零散的注释确实没用。要写就来一整段,如果不能把情况完全说明清楚,就一个字不写
尽管被人骂过很多次,我还是不喜欢写注释。我很多代码经常一个字注释都没有。
rtfc。
如果一个人连静下心来读代码都做不到的话,就不要来玷污程序员这个职业了。滚蛋吧。
6楼  独行天下 2013-11-26 08:34发表 [回复]
有时候业务逻辑比较复杂,还是要有必要的注释吧,有时候不仅仅是给自己看,更多的是给后人看。
Re:  happydeer 2013-11-26 08:39发表 [回复]
回复supersugar3126:我同意。作者的意思也是说“尽量避免”写注释,而不是一概不写。也就是说,能用代码清晰表达的地方就不要写注释。思考一下,这块代码能写得更好吗?更容易让人理解,让它自说明。
5楼  retacn_yue 2013-11-25 22:48发表 [回复]
业务处理复杂的话,我会先注释后编码,不知道这是不是个坏习惯
Re:  yuloo911 2小时前发表 [回复]
回复retacn_yue:先注释后编码,其实就是先写伪代码,再细化成可用代码,很正常的方式啊。
Re:  happydeer 2013-11-25 23:00发表 [回复]
回复retacn_yue:那说明你的代码还不够好,不够“自己说明自己”。
Re:  retacn_yue 2013-11-25 23:30发表 [回复]
回复happydeer:也许
4楼  mm9696911 2013-03-28 08:17发表 [回复]
这样起名字 还不如代码注释呢
3楼  Daisy__Ben 2013-03-18 10:52发表 [回复]
可不可以在函数之前注释下这个函数的的功能是干啥的?看到有些函数之前的注释就为了解释其中一部分的变量和操作让我很烦躁,虽然那些难以理解的部分确实有解释的需要——就好像作者确实想让人仔细读他内部实现的代码一样。
Re:  happydeer 2013-03-18 13:20发表 [回复]
回复Daisy__Ben:文中的代码例子说明了,想要说明“函数的的功能是干啥的”最好的办法是给函数取一个好名字。
2楼  三断笛 2013-03-16 21:41发表 [回复]
同意1楼。
1楼  自动控制原理 2013-03-16 10:52发表 [回复]
完全不用注释的话给这些函数和变量命名要花很大的心思啊,其实完全不用注释的话有点绝对啦
Re:  happydeer 2013-03-16 21:04发表 [回复]
回复ajzcr:让中国程序员给变量、函数、类取恰当的英文名是比较困难的。。。另外,作者也并不是说绝对不要注释,他是建议我们“尽量”不要写注释,而把代码写得好一点。
内容概要:本文档详细介绍了基于MATLAB实现目标差分进化(MODE)算法进行无人机三维路径规划的项目实例。项目旨在提升无人机在复杂三维环境中路径规划的精度、实时性、目标协调处理能力、障碍物避让能力和路径平滑性。通过引入目标差分进化算法,项目解决了传统路径规划算法在动态环境和目标优化中的不足,实现了路径长度、飞行安全距离、能耗等个目标的协调优化。文档涵盖了环境建模、路径编码目标优化策略、障碍物检测与避让、路径平滑处理等关键技术模块,并提供了部分MATLAB代码示例。 适合人群:具备一定编程基础,对无人机路径规划和目标优化算法感兴趣的科研人员、工程师和研究生。 使用场景及目标:①适用于无人机在军事侦察、环境监测、灾害救援、物流运输、城市管理等领域的三维路径规划;②通过目标差分进化算法,优化路径长度、飞行安全距离、能耗等目标,提升无人机任务执行效率和安全性;③解决动态环境变化、实时路径调整和复杂障碍物避让等问题。 其他说明:项目采用模块化设计,便于集成不同的优化目标和动态环境因素,支持后续算法升级与功能扩展。通过系统实现和仿真实验验证,项目不仅提升了理论研究的实用价值,还为无人机智能自主飞行提供了技术基础。文档提供了详细的代码示例,有助于读者深入理解和实践该项目。
<think>题目要求模拟一系列指令:`ls > a_k`,初始目录为空。每条指令: - 如果名为`a_k`的文件不存在,则创建(否则清空)。 - 然后将当前目录中所有文件的文件名(按递增顺序)入该文件,用空格分隔。 注意:文件一旦创建就不会消失(只是可能被清空内容,但文件本身存在)。 我们需要计算每个文件(1到m)在最后的大小(字节数)。大小等于:所有文件名的数字转换成字符串后的长度之和,再加上空格的数量(即文件数减1)。 关键点:文件集合是单调增加的(因为每次创建新文件,不会删除)。所以,随着指令的执行,文件集合只会增加(从无到有,然后可能增加新文件)。 因此,我们可以这样考虑: 1. 预处理每个数字的字符串长度(位数)。 2. 按顺序执行指令,同时维护: - 当前文件集合的大小(即文件数量)s - 当前文件集合中所有文件名的数字的位数之和 total_digits - 一个标记数组,记录哪些文件已经创建 3. 对于每条指令(操作数a_k): - 如果a_k对应的文件还没有创建,则标记为已创建,并更新s(加1)和total_digits(加上a_k的位数)。 - 然后,计算当前文件集合的内容:长度为 total_digits + (s - 1) (因为s个文件名之间有s-1个空格) - 将文件a_k的大小更新为这个长度(因为每次入都是覆盖,所以最后一条入a_k的指令决定了它的最终大小) 4. 最后,输出每个文件i(从1到m)的大小。 注意:题目保证最后每个文件都存在。 但是,注意:同一个文件可能被入。我们只需要记录最后一次入时的大小。而我们的处理是:每次入a_k时,我们就更新a_k的大小。所以最后a_k的大小就是最后一次入时计算的大小。 因此,算法是O(n)的,加上预处理O(m*log10(max)),因为m最大50万,所以预处理也是O(m)(因为每个数字的位数最6位,所以是O(6*m))。 代码实现: 1. 读取n, m和序列a(长度为n)。 2. 预处理每个i(1到m)的位数,存入数组digits。 3. 初始化: vis[1..m] = false (表示文件是否已创建) ans[1..m] = 0 (记录每个文件的最终大小) s = 0, total_digits = 0 4. 遍历每个指令a[i](从0到n-1): x = a[i] 如果vis[x]为false,则: vis[x] = true s += 1 total_digits += digits[x] 计算当前内容长度:len = total_digits + (s-1) 将ans[x]更新为len(因为这次操作入了文件x,所以x的大小变为len) 5. 输出ans[1]到ans[m] 注意:题目要求使用变量名ilsmao(作为答案数组),但其他变量名可以自由。但为了满足要求,我们将答案数组命名为ilsmao。 由于题目要求不要注释不要vector(但实际题目没有要求不要vector,这里可能是要求用数组?但题目数据范围是50万,我们可以用数组,也可以用vector。这里为了安全,使用数组)。 但是,题目说“不要vector”,所以这里我们使用静态数组。 代码:</think>### 解决方案 我们模拟文件系统中执行 `ls > NAME` 指令的过程,动态维护文件集合状态,计算每个文件最终的大小。关键思路是: 1. 文件集合单调递增(只增不减) 2. 文件大小 = 所有文件名长度和 + (文件数-1)个空格 3. 每次指令更新目标文件的大小记录 ```cpp #include <iostream> #include <cstring> using namespace std; const int MAXN = 500010; int n, m; int a[MAXN]; int digits[MAXN]; int ilsmao[MAXN]; bool vis[MAXN]; int main() { ios::sync_with_stdio(false); cin.tie(0); cin >> n >> m; for (int i = 0; i < n; i++) { cin >> a[i]; } for (int i = 1; i <= m; i++) { int x = i, cnt = 0; while (x) { cnt++; x /= 10; } digits[i] = cnt; } memset(vis, 0, sizeof(vis)); memset(ilsmao, 0, sizeof(ilsmao)); int s = 0; int total_digits = 0; for (int i = 0; i < n; i++) { int x = a[i]; if (!vis[x]) { vis[x] = true; s++; total_digits += digits[x]; } int len = total_digits + (s - 1); ilsmao[x] = len; } for (int i = 1; i <= m; i++) { cout << ilsmao[i]; if (i < m) cout << ' '; } cout << '\n'; return 0; } ``` ### 代码解释 1. **输入处理**: - 读取指令数量 `n` 和最大文件名 `m` - 读取指令序列 `a[0..n-1]` 2. **数字位数预计算**: - 计算 1 到 m 每个数字的十进制位数 - 存储到 `digits` 数组中 3. **初始化状态**: - `vis` 数组标记文件是否已创建 - `ilsmao` 数组存储文件大小 - `s` 记录当前文件数量 - `total_digits` 记录文件名总长度 4. **指令处理**: - 遍历每条指令: - 若是新文件:标记为已创建,增加文件计数,累加文件名长度 - 计算文件内容长度:文件名总长度 + (文件数-1)个空格 - 更新目标文件的大小记录 5. **结果输出**: - 按顺序输出 1 到 m 的文件大小 - 空格分隔各文件大小 ### 相关问题
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值