Logo
Section 1 / 10

优化环境

学会充分利用你的编程环境。

导入

优化环境

学会充分利用你的编程环境。

让我们从头开始:当需要工作时,你会启动哪个程序?是 Visual Studio、Emacs,还是终端窗口?在编写代码之前,我们首先面对的是编程环境。这个环境包括我们的计算机、文本编辑器、编译器、调试器等。

我们很可能只使用了这些工具所提供功能的一小部分。只需投入少量精力,就能显著提升效率。

文本编辑器

我的一位机械师同事常调侃程序员:“你们的工作是最轻松的,不就是敲键盘吗?”确实,我们花大量时间在打字上。当然,编程还有一些其他细节——比如程序员对元音字母有种奇怪的排斥,而对分号却情有独钟——但有时候,真正的瓶颈其实就是把字符输入到屏幕上。

既然我们花了这么多时间打字,如果能让文本编辑器帮我们完成一些重复性工作,哪怕节省10%的努力,一年下来也能省下可观的时间。

程序员的编辑器

观察一位经验丰富的程序员工作,你最先注意到的不是他们代码的精妙,而是他们操作代码的娴熟。一位熟练的程序员似乎总能“快过电脑”:即使在瞬间将代码从一处移动到另一处的同时,他们已经在思考五步之后的动作,甚至可以闭上眼睛,让手指自然跟上思路。

无论选择哪一代编辑器,优秀的编辑器都应具备以下共同特性:

  • 以键盘为主的操作方式:鼠标可有可无。掌握常用操作的快捷键至关重要,因为这比频繁伸手去拿鼠标快得多。这也是为什么老派的 Unix 编辑器至今仍受欢迎——它们对键盘的利用非常高效。

  • 复杂的移动与选择工具:程序员的编辑器足够智能,不仅能按行和列移动,还能识别代码中的逻辑块。当光标位于某个代码块时,只需几个按键即可选中并移动该块,全程无需碰鼠标。

  • 语言感知的语法高亮:有些人认为这有助于看清代码的整体结构;也有人并不在意。但至少,它能让代码看起来更美观。

  • 语言感知的自动缩进:完全没有必要反复按空格键来缩进每一行。好的编辑器会根据编程语言的规则和你的风格偏好,自动处理缩进。

  • 文本补全功能:一旦输入了一个较长的变量名或函数名,就无需再次完整输入。编辑器支持输入部分名称后按下特定键,即可自动补全剩余内容。对于模糊匹配的部分,连续按补全键可循环切换候选项。

提升编码速度的第二个关键在于投入时间。高级编辑器的学习曲线较陡,因此建议每周抽出一点时间学习一个新技巧。不能一蹴而就,因为这些技巧必须融入肌肉记忆。换句话说,我们要让这些操作成为本能反应——手指自动执行,无需意识参与,这样我们的注意力才能专注于代码本身。

最后,多观察资深同事如何使用他们的编辑器。不妨尝试与他们进行结对编程——每当想到“原来还能这样!”时,立刻记下来,弄清楚他们是怎么做到的。

通过安全外壳(SSH)进行编辑

在程序员的日常工作中,经常需要通过简单的 SSH、串口或其他纯文本控制台访问远程机器。这时,我们需要掌握一款能在纯文本模式下运行的编辑器。无论本地 GUI 编辑器有多炫酷,当你身处远程终端时,这些功能都毫无意义。

为此,我们推荐使用 vi。默认情况下,vi 几乎存在于所有类 Unix 系统中;而其他编辑器(如 Emacs)则不一定存在。因此,至少花一个小时学会基本的 vi 操作,足以应对简单修改。这将在未来服务器半崩溃、仅能进入单用户控制台时派上大用场。

语言工具

我们在《精通你的编程语言》一节中会深入探讨编程语言,但在此先简要讨论一下环境中常见的语言工具。这些通常包括编译器、调试器或解释器。IDE 中的“构建”按钮会调用构建系统,通常是编译代码。不管是什么工具,我们都应熟悉其快捷键,不要每次构建都去寻找鼠标。

有些语言没有编译器,而是采用解释执行,这类语言通常提供一个 读取-求值-打印-循环(REPL) 环境。我们可以直接输入表达式并立即看到结果。REPL 是极佳的时间节省工具,因为它让我们无需运行整个程序就能快速验证想法。例如,若需对数据进行复杂转换,可以直接打开 REPL,用示例数据尝试各种方法。

某些开发环境还以新颖的方式整合了这些工具。例如,将 REPL 嵌入文本编辑器,允许我们选中当前行,由语言解释器即时求值,并将结果直接显示在同一窗口中。这种即时反馈带来极大的满足感。

此外,环境还可集成语言感知的重构功能,例如同时重命名一个方法及其所有调用点。背后,环境持续编译或解释代码,因此重构具有真正的智能,而非简单的全局查找替换。

调试器

许多开发环境都内置了源码级调试器。它允许我们在程序因崩溃或主动设置断点而暂停时,检查程序状态,如调用栈、变量值等。在 C 这类语言中,崩溃常提示“段错误”这种毫无帮助的信息,此时调试器几乎是获取基础信息的唯一途径。更高级的语言通常会自动输出调用栈,有时这已足够定位问题。

不过,调试器也有代价:它会减慢程序运行速度。这可能导致原本不存在的时序问题出现,或使已存在的问题消失。这类问题被亲切地称为 海森堡虫(Heisenbugs)——你无法同时观察和调试它们。

根据平台不同,调试器还能协助事后分析。例如,在 Linux 上运行的 C 程序崩溃时,可能生成一个核心转储文件(core file),其中包含系统内存的快照。调试器可加载该文件,告诉我们崩溃时刻线程和变量的状态。

注:“核心”一词源于磁芯存储器,这是一种上世纪70年代前使用的早期内存形式,现已淘汰。但“核心”一词仍被沿用,泛指内存。

如果你擅长编写单元测试,可能很少需要用到调试器,因为大多数 bug 都会在测试阶段被发现。但某些类型的 bug,尤其是涉及 I/O 或线程时序的问题,很难通过自动化测试捕捉。对于这类问题,我们只能寄希望于一份良好的核心转储文件或堆栈跟踪信息。

性能分析

当程序逻辑正确但运行太慢时该怎么办?高德纳曾说:“过早优化是万恶之源。” 因此,应优先确保代码正确。只有在确认存在性能问题后,才应测量问题所在,再着手解决。

这就是性能分析器的作用:它告诉我们每个函数被调用了多少次,以及在每个函数中花费了多少时间。结果往往令人意外。例如,我曾分析过一个小型数独求解器,发现它大部分时间都在遍历单元格列表。通过引入缓存机制,运行速度提升了3000倍。

最令人困惑的性能问题,往往是程序大部分时间处于空闲状态。这通常是因为资源竞争所致。性能分析器会显示程序在锁定资源的函数上耗费了大量时间。或者,也可能根本不是程序的问题——可能是网络资源响应缓慢,分析器会显示程序正在等待网络接收数据。

生产环境程序

流利地使用该语言