☰
LaTeX论文排版实战:伪代码与代码块的高效写法
2026/10/2 15:14:35 网站建设 项目流程

写论文最头疼的事情之一,就是算法伪代码和代码块怎么排版。用Word排版过算法的人应该都有体会,缩进对不齐、行号乱了、代码高亮一塌糊涂,光是调整格式就能耗掉一下午。转用LaTeX之后才发现,这些事情其实有非常成熟的解决方案。这篇笔记就专门聊聊LaTeX里的伪代码和代码块到底怎么写,从宏包选型到实际配置,再到我踩过的各种坑,一次性讲清楚。

1. 先把环境准备好:伪代码和代码块的运行前提

1.1 编译器与宏包选择

先说明一点,伪代码和代码块的排版并不需要什么特殊的编译器,常规的pdfLaTeX、XeLaTeX都能跑。但如果你要往代码块里写中文注释,或者伪代码里本身就要用到中文字符,那建议直接用XeLaTeX搭配ctex宏包,省去一堆字体配置的麻烦。我目前用的是TeX Live,安装的时候直接全量安装,虽然占了几个G的硬盘,但好处是宏包基本齐全,不用临时缺什么再补装什么。

这里顺便提一下安装。很多新手卡在第一步,其实LaTeX发行版就选两个主流:TeX Live(跨平台,更新快)和MiKTeX(Windows下体验不错,按需自动安装宏包)。我个人的建议是直接用TeX Live,因为写论文经常要投不同期刊的模板,TeX Live对各类宏包的支持更完整,编译兼容性也更好。安装完记得在终端里跑一下latex -v,看到版本信息就说明环境已经通了。

1.2 在VSCode里快速搭建编写环境

编辑器我用的是VSCode,配合LaTeX Workshop插件,体验非常接近IDE。装好插件之后,还需要做两件事:第一,确认插件能自动识别你的TeX发行版,LaTeX Workshop默认会去系统PATH里找latexmk,只要你安装了TeX Live并加入了PATH,一般就能直接编译;第二,配置编译工具链,在settings.json里加上这么一段:

{ "latex-workshop.latex.recipes": [{ "name": "xelatex", "tools": ["xelatex"] }], "latex-workshop.latex.tools": [{ "name": "xelatex", "command": "xelatex", "args": [ "-synctex=1", "-interaction=nonstopmode", "-file-line-error", "%DOC%" ] }] }

用XeLaTeX作为默认编译器,主要是为了上面的中文支持和字体处理。VSCode配上LaTeX Workshop之后,保存自动编译、PDF预览、SyncTeX正反向定位都齐了,写伪代码和代码块的时候,编译反馈是即时的,定位报错也很方便。个人建议开-interaction=nonstopmode,这样遇到错误不会停下来等你输入指令,而是直接往下跑,最后统一看日志,对新手来说更友好。

2. 伪代码方案选型:algorithm、algorithmic与algpseudocode怎么选

2.1 三种主流宏包的定位与区别

LaTeX里写伪代码,绕不开几个经典的宏包组合。很多新手一上来就被algorithm、algorithmic、algorithmicx、algpseudocode、algpascal这些名字搞混了。我花过一段时间把它们的区别理清楚了,其实没那么复杂。

algorithm宏包是一个浮动体包装器,它提供一个类似于table、figure的算法环境,用来给算法加编号、加标题(\caption)、生成目录条目。真正管算法内部排版的是另外一个宏包。

algorithmic宏包是最老的算法排版工具,命令风格是\IF、\FOR、\REPEAT这种全大写、带反斜杠的写法,语法接近原始的算法描述语言,但灵活性差一些,缩进和块结构控制不够精细。

algorithmicx(以及它衍生出的algpseudocode、algpascal、algc等)是改进版本,核心是algpseudocode提供的语法,命令变成了\If、\For、\While这样首字母大写的形式,块结构用\EndIf、\EndFor来关闭,配合\State逐行写语句。整体写起来更像是在写伪代码本身,而不是在被宏包的语法折腾。

所以我的选型结论是:直接用algorithm加algpseudocode这个组合,也就是algorithm做浮动体外壳,algpseudocode负责内部排版。这也是目前期刊论文里最常见的搭配。

2.2 按行号、缩进与控制流:algpseudocode的具体写法

经历过几种宏包的对比之后,我把实际写论文经常用到的模板总结了一下。需要在导言区引入这几个宏包:

\usepackage{algorithm} \usepackage{algpseudocode}

一个标准算法的写法是下面这样的:

\begin{algorithm} \caption{基于贪心策略的资源调度算法} \label{alg:greedy} \begin{algorithmic}[1] \Require 任务集合 $T$,资源集合 $R$ \Ensure 调度方案 $S$ \State 初始化 $S \leftarrow \emptyset$ \For{每个任务 $t \in T$} \State 按优先级从高到低排序候选资源 \For{每个候选资源 $r \in R$} \If{$r$ 满足 $t$ 的约束条件} \State 将 $t$ 分配给 $r$ \State $S \leftarrow S \cup \{(t, r)\}$ \State \textbf{break} \EndIf \EndFor \EndFor \State \Return $S$ \end{algorithmic} \end{algorithm}

有两个细节值得展开。第一,\begin{algorithmic}[1]里的参数[1]表示按行编号,而且编号的粒度是每一行语句;如果不传参数,默认不显示行号。有些模板要求算法行号连续编号,有些要求按算法独立编号,这取决于期刊的要求,用[1]一般是最稳妥的。

第二,\State是algpseudocode的基石命令,不管是一条赋值语句、一个函数调用,还是一句注释,基本都要用\State开头。控制流语句(\If、\For、\While、\Repeat、\Function)会自动调整缩进和块结构,你只需要正确地闭合它们。比如\If对应\EndIf,\For对应\EndFor,\Function对应\EndFunction。如果漏掉了一个\EndIf,编译报错会指向整个algorithmic环境,而不是精确到某一行,这个排错路径我在后面第4部分细讲。

2.3 函数、输入输出与跨栏问题的处理

写伪代码的时候,函数定义和输入输出是很常见的需求。algpseudocode提供了一组直接可用的命令:

\begin{algorithmic}[1] \Function{MinCost}{$G$, $s$} \State $dist \leftarrow \infty$ \Comment{初始化距离数组} \For{$v$ in $G.vertices$} \State $dist[v] \leftarrow \infty$ \EndFor \State $dist[s] \leftarrow 0$ \State $Q \leftarrow G.vertices$ \While{$Q \neq \emptyset$} \State $u \leftarrow$ ExtractMin($Q$) \For{each neighbor $v$ of $u$} \If{$dist[u] + w(u,v) < dist[v]$} \State $dist[v] \leftarrow dist[u] + w(u,v)$ \EndIf \EndFor \EndWhile \State \Return $dist$ \EndFunction \end{algorithmic}

\Function自动处理函数名和参数的排版,\Comment可以加行内注释,\Require和\Ensure则分别对应算法开始前的输入、输出说明,也就是类似"Require: xxx / Ensure: xxx"的那两行。这套语法基本覆盖了论文里90%的算法描述场景。

另外一个常见需求是跨栏问题。如果你在用双栏模板写论文,某个伪代码太长,单栏放不下,但你又不想让它被截断,可以给algorithm环境加上*号,变成\begin{algorithm*},这样它就会横跨两栏。不过跨栏算法的位置浮动行为会比较难控制,有时候会跑到下一页的顶部,建议配合t位置参数使用:\begin{algorithm*}[t],尽量让它出现在当前页顶部。

还有一点关于标题的细节。用\caption{}管理算法标题之后,默认会显示"Algorithm 1: 标题"的形式。如果你的期刊要求显示成"算法1",需要额外配置,常见的写法是在导言区加上:

\renewcommand{\algorithmcfname}{算法}

或者根据你用的具体宏包版本做相应的\floatname设置。这类细节往往决定了投稿时能不能通过格式审查,很值得提前处理。

3. 代码块排版实战:listings与minted

3.1 listings宏包的通用配置思路

伪代码解决之后,代码块又是另一块硬骨头。LaTeX里展示代码块有两个主流方案:listings和minted。listings的优势是完全依赖LaTeX本身,不需要额外的外部工具,兼容性最好;minted依靠Python的Pygments库做语法高亮,颜色和样式更精致,但编译时必须要加-shell-escape参数,否则会报错。

我平时写博客、做技术笔记,用的是listings,因为环境简单、不容易出幺蛾子。先用一个案例说明基本配置,把下面这段放在导言区:

\usepackage{listings} \usepackage{xcolor} \lstdefinestyle{mycode}{ language=Python, basicstyle=\ttfamily\small, keywordstyle=\color{blue}, commentstyle=\color{gray}, stringstyle=\color{red}, numbers=left, numberstyle=\tiny\color{gray}, frame=single, rulecolor=\color{black}, breaklines=true, showstringspaces=false, tabsize=4, captionpos=b } \lstset{style=mycode}

这样定义好样式之后,正文里用:

\begin{lstlisting}[language=Python, caption=示例代码] def hello(): print("Hello, LaTeX!") \end{lstlisting}

简单解释一下几个关键参数。basicstyle控制代码字体,一般用等宽的\ttfamily;keywordstyle、commentstyle、stringstyle分别设置关键字、注释、字符串的颜色;breaklines=true允许自动断行,这是处理长代码行的救命配置;showstringspaces=false避免字符串里的空格显示成特殊符号。语言类型可以根据需要改,listings支持的语言相当多,常见的Python、Java、C/C++、Matlab、R、SQL都在支持列表里。

3.2 让代码块支持中文与特殊符号

listings有个很经典的坑:默认情况下,lstlisting环境里的中文注释会编译成乱码,或者直接报错。如果你用的是XeLaTeX编译,解决办法比较直接,给\lstset加上:

\lstset{ extendedchars=true, inputencoding=utf8 }

但更稳妥的方式是设置literate选项,手动告诉listings如何显示中文。我常用的配置是这样:

\lstset{ literate={中文}{{中文}}1 {注释}{{注释}}1 {代码}{{代码}}1 }

这种方式适合中文注释只出现在少数固定的词里。如果代码里中文注释太多、内容不固定,我建议干脆换minted,或者把中文注释改成英文。说实话,期刊论文的代码块注释通常不会太长,英文注释是最省事的方案。

还有一个经常遇到的问题,listings环境和文本之间的空格。默认情况下,\begin{lstlisting}前面有没有缩进、后面有没有空行,会直接影响代码块的显示位置。如果不小心在\end{lstlisting}后面留了空格,版面上会出现多余的空隙。我在自己的笔记里总结的规则是:lstlisting环境前后都保持顶格写,不要缩进,这样最不容易出问题。

3.3 跨页、断行与高亮:几个救命配置

写技术文档的时候,一段完整的代码往往会超过一页,listings默认会直接截断,但截断的位置没有提示,看起来很难受。这时可以加个frame配合跨页处理,或者用特殊的省略标记。我常用的做法是:

\lstset{ breaklines=true, prebreak=\raisebox{0ex}[0ex][0ex]{\ensuremath{\hookleftarrow}}, postbreak=\raisebox{0ex}[0ex][0ex]{\ensuremath{\hookrightarrow}}, breakatwhitespace=false }

这样长代码在断行处会显示一个向左或向右的箭头,读者一看就知道这一行还没结束。breakatwhitespace=false的意思是允许在任意字符处断行,而不仅仅是空格位置。

如果你用minted,跨页问题有另外一个思路。minted本身不支持直接分页,通常要配合tcolorbox使用。我也试过几次,这个组合的效果确实漂亮,能做出带背景色、圆角边框的代码块,但配置相对复杂,还需要确保系统装了Python环境和Pygments库。判断标准很直接:本地写给自己看的笔记,listings完全够用;要做成演示文档或者课程材料,再考虑上minted。

4. 常见问题与排查技巧实录

4.1 编译报错“Environment algorithm undefined”之类怎么办

这个报错是典型的宏包缺失。algorithm环境在algorithm宏包里,算法内部的排版指令在algpseudocode宏包里。你只用了algpseudocode却不引入algorithm,就会出现Environment algorithm undefined。解决办法就是在导言区同时引入这两个。还有一种情况是,你用了\usepackage{algorithmic}而不是\usepackage{algpseudocode},那么\If、\For这些命令都识别不了,因为老版algorithmic宏包用的是全大写的\IF、\FOR。所以遇到报错先别慌,看一眼宏包名是不是搞混了。

4.2 换行符、空格、特殊字符的那些坑

LaTeX文章主体里,空行控制段落间距,输入多个空格会被压缩成一个空格。但在lstlisting环境内部,所有空格和换行都会原样保留,这是代码块排版区别于普通正文的最大特点。如果你发现代码块里的缩进消失了,检查一下是不是用了listings但没设置tabsize,或者代码里用了Tab而listings默认不识别Tab宽度。我一般统一设置tabsize=4,并且代码文件里全部用空格代替Tab,这样在LaTeX和其他编辑器之间切换都不会乱。

伪代码环境里则相反,\State后面的语句里,如果要显示多条语句或特殊符号,需要自己控制分隔。比如表示赋值,建议用\leftarrow而不是直接打一个等号,这样类别更清晰;等于判断则用=。不要混淆这两个符号的语义。还有,%在LaTeX里是注释符,想在伪代码或代码里显示百分号,必须写成\%。我第一次写带百分号的伪代码时,编译通过但后面的内容全消失了,找了好久才发现是%惹的祸。

4.3 特殊字符与符号映射的快捷参考

这里把常见字符在LaTeX里的正确写法直接列出来,省得每次都去查。表格可以当速查手册用:

显示效果LaTeX写法适用环境
赋值箭头$\leftarrow$伪代码
返回箭头$\rightarrow$伪代码
小于等于$\leq$伪代码/数学
不等于$\neq$伪代码/数学
百分号\%全部
井号\#全部
下划线\_全部
花括号\{\}全部
反斜杠\textbackslash代码块/文本
波浪线\textasciitilde代码块/文本

这个表格是我自己整理网上宏包符号教程时精简出来的,基本覆盖了写算法和代码块时最常用的一批特殊字符。出现"符号无法显示"的问题时,先查这个表。

4.4 个人踩坑记录:从报错到稳定出图的排查顺序

我刚开始用LaTeX排伪代码的时候,遇到一个特别奇怪的现象:algorithm环境里明明写了\caption{xxx},但生成的PDF里标题显示成"Algorithm 1: xxx",而不是我自己写的文字。查了半天才发现是模板引入了一个algorithm2e宏包,它和algorithm宏包的命令冲突,导致\caption格式化被覆盖了。解决方法是二选一,只保留一套宏包体系。后来我干脆在写论文模板适配时,先翻一下主模板的宏包列表,再决定用哪个方案。

踩过的另一个坑是,algpseudocode的\Function命令在函数名带数字下标时,有时候会编译报错。原因在于函数名里的下划线被当成了数学下标标记。正确的做法是把函数名用\text包一层,比如\Function{Min\_Cost}{}要写成\Function{\text{Min\_Cost}}{}。这个坑特别隐蔽,因为单看代码很容易忽略。

排查编译问题的时候,我习惯按这样的顺序来:先看VSCode的LaTeX Workshop输出窗口里的错误日志,找出第一个Error;然后检查对应的宏包是否存在、是否冲突;再看是不是特殊字符没有转义。大多数问题都在这三步里就能定位。如果日志看不懂,我会把文档拆成最小例子,只留一个伪代码或一个代码块,一段段往上加,加一次编译一次,直到复现报错位置。这个方法虽然笨,但在排查宏包冲突时特别管用。

4.5 重要提示:浮动体与文字错位问题

最后一个常见问题不是报错,而是排版不理想。algorithm环境是浮动体,跟你用table、figure一样,它会被LaTeX自动挪到页面的某个位置,不保证出现在源码编辑位置的正下方。如果你看到"这里留了一大片空白,算法跑到下一页去了",别急着怀疑代码,先调整位置参数,改成\begin{algorithm}[htbp],让LaTeX有更多选择余地。如果还是不满意,可以用[H]强制放在当前位置,但需要额外引入float宏包:

\usepackage{float} ... \begin{algorithm}[H] ... \end{algorithm}

我用[H]的时候比较克制,因为它会牺牲一些排版美感,但对论文初稿的审阅阶段很实用,毕竟先看到完整内容比纠结位置更重要。

5. 从笔记到项目:一次完整的练习

说了这么多,光看不如动手。我建议你新建一个测试文档,把伪代码、代码块、跨栏算法、断行配置全部放进去编译一遍。我自己的练习模板大概是这样的,给你做个参考:

\documentclass[11pt]{article} \usepackage[UTF8]{ctex} \usepackage{algorithm} \usepackage{algpseudocode} \usepackage{listings} \usepackage{xcolor} \usepackage{float} \lstset{ language=Python, basicstyle=\ttfamily\footnotesize, breaklines=true, showstringspaces=false, tabsize=4 } \begin{document} \section{伪代码示例} \begin{algorithm}[H] \caption{快速排序} \label{alg:quicksort} \begin{algorithmic}[1] \Function{QuickSort}{$A, low, high$} \If{$low < high$} \State $p \leftarrow \text{Partition}(A, low, high)$ \State \Call{QuickSort}{$A, low, p-1$} \State \Call{QuickSort}{$A, p+1, high$} \EndIf \EndFunction \end{algorithmic} \end{algorithm} \section{代码块示例} \begin{lstlisting}[caption=Python示例] def quick_sort(arr): if len(arr) <= 1: return arr pivot = arr[len(arr) // 2] left = [x for x in arr if x < pivot] middle = [x for x in arr if x == pivot] right = [x for x in arr if x > pivot] return quick_sort(left) + middle + quick_sort(right) \end{lstlisting} \end{document}

这个文档直接保存成.tex,用XeLaTeX编译,一次就能通过。如果你在编译过程中遇到了任何一条本文提到的报错,再回头看对应的章节,基本上都能找到解决方案。我个人在实际操作中的体会是,LaTeX的伪代码和代码块排版,真正的难点从来不是宏包不够用,而是宏包选型不统一、符号转义漏掉、浮动体位置控制不好这三个老问题。把这三关过了,剩下的都是熟能生巧。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询