LaTeX 算法与代码环境
算法环境 algorithm2e
在LaTeX中使用伪代码来描述算法是常见的需求,LaTeX其实有很多类似名称的宏包,简单辨析一下:(参考知乎上的一个回答)
- algorithm 用来封装算法:给算法加上标题(caption)和标签(label),方便进行索引;
- algorithmic 相当于算法的内容物;
- algorithmicx 相当于 algorithmic 的升级版;
- algpseudocode 是 algorithmicx 的一种呈现方式,完整包含了algorithmicx;
- algorithm2e 和 algorithmicx、algorithmic 类似,也是用来描述算法的;但是其语法不如 algorithmicx 直白。
总得来说通常有两条路子可以选择:
- 用algorithm2e宏包搞定
- 用algpseudocode或algorithmic等撰写算法本身,再用algorithm包给算法加标题
下面主要考虑第一个方式,即使用algorithm2e宏包。
导入algorithm2e宏包时通常顺便加上如下选项 1
2
3\usepackage[ruled,linesnumbered]{algorithm2e}
% ruled 上下添加横线,类似于三线表,此时标题在上面
% linesnumbered 显示行数
算法的主体框架如下,可以添加标题(caption)和标签(label)
1
2
3
4
5
6\begin{algorithm}
...
\caption{XXX}\label{alg1}
\end{algorithm}
一个简单的例子: 1
2
3
4
5
6
7
8
9
10
11\begin{algorithm}
\caption{Euclid's algorithm}
\KwData{Two nonnegative integers $a$ and $b$}
\KwResult{Their greatest common divisor $d = \gcd(a, b)$}
\While{$b \neq 0$}{
$r \leftarrow a \bmod b$\;
$a \leftarrow b$\;
$b \leftarrow r$\;
}
$d \leftarrow a$\;
\end{algorithm}
算法的每一行语句可以使用\;
结尾,这会在文本中加上分号结尾并换行,还可以使用\\
强行换行。
除此之外,下面的某些固定语法结构也会导致换行。
算法内部需要数学表达式时,仍然需要$$
等来创建数学环境。
算法通常需要使用下面这些预定义宏:
IO:
- 输入:
\KwIn{<input>}
- 数据:
\KwData{<input>}
- 输出:
\KwOut{<output>}
- 结果:
\KwResult{<output>}
- 输入:
基础:
- to:
\KwTo
- 返回值:
\KeRet{<value>}
或等效的\Return{<value>}
- to:
循环:包括下面几种形式,其中含
l-
前缀的版本是行内形式,即内部不产生换行(但结尾仍然有换行)1
2
3
4
5
6
7
8
9\For{<condition>}{<text loop>}
\While{<condition>}{<text loop>}
\ForEach{<condition>}{<text loop>}
\ForAll{<condition>}{<text loop>}
\lFor{<condition>}{<text loop>}
\lWhile{<condition>}{<text loop>}
\lForEach{<condition>}{<text loop>}
\lForAll{<condition>}{<text loop>}条件:包括下面几种形式,默认形式会产生换行并使用end结尾;含
l-
前缀的版本是行内形式,即内部不产生换行(但结尾仍然有换行);含e-
前缀的eIf自带真假两个分支;含u-
前缀的版本是未完成形式,即结尾不含end,需要搭配其他命令来收尾1
2
3
4
5
6
7
8
9
10
11
12
13
14
15\If{<condition>}{<then block>}
\ElseIf{<elseif block>}{<then block>}
\Else{<else block>}
\lIf{<condition>}{<then block>}
\lElseIf{<elseif block>}{<then block>}
\lElse{<else block>}
\uIf{<condition>}{<then block>}
\uElseIf{<elseif block>}{<then block>}
\uElse{<else block>}
\eIf{<condition>}{<then block>}{<else block>}
\leIf{<condition>}{<then block>}{<else block>}注释:默认支持两类注释,即
tcc
(C语言风格/**/
)和tcp
(C++风格//
)注释,有几个选项会调整注释的细节,这里略去1
2
3
4
5
6
7
8
9
10
11
12
13\tcc{<comment>}
\tcc*{<comment>}
\tcc*[r]{<comment>}
\tcc*[l]{<comment>}
\tcc*[h]{<comment>}
\tcc*[f]{<comment>}
\tcp{<comment>}
\tcp*{<comment>}
\tcp*[r]{<comment>}
\tcp*[l]{<comment>}
\tcp*[h]{<comment>}
\tcp*[f]{<comment>}
对于循环和条件结构的呈现,还涉及到如下几个选项:
- lined:伪代码的 start-end 之间用竖线相连
- vlined:伪代码的 start-end 之间用竖折线相连,其中折线代替了end的作用
- noline:伪代码的 start-end 之间没有线相连
这几个选项既可以在宏包选项中设置,也可以使用下面的命令设置
1
2
3\SetAlgoLined
\SetAlgoVlined
\SetAlgoNoline
在循环和条件结构中,都支持加上括号包裹的注释选项,例如
1
2
3
4
5
6
7
8
9
10
11
12
13\lIf(\tcc*[h]{lif comment}){test}{
text
}
\uIf(\tcc*[f]{uif comment}){test}{
then text\;
}
\uElseIf(\tcc*[f]{comment}){test}{
elseif text\;
}
\lElseIf(\tcc*[h]{comment}){test}{
text
}
\lElse(\tcc*[f]{comment}){text}
宏包还支持自定义一些宏,例如下面定义了与IO宏类似的一个命令\KwPara{<input>}
,代表算法需要的各种细节参数
1
2
3\SetKwInput{KwPara}{Parameter}
\KwPara{m,n,$\varepsilon$}
除了这个宏包之外,还可以使用其他方式来实现,例如导入algorithm和algpseudocode这两个宏包,本文暂不讨论具体细节,不过可以明确的是,它的命令风格与algorithm2e是截然不同的。
代码环境
整理一下LaTeX关于源代码显示的命令,注意这与算法(伪代码)环境的需求是不一样的。
抄录环境 verbatim
LaTeX直接提供verbatim抄录环境,在其中以等宽字体原样排版代码,回车和空格是正常的换行和空位的作用,缩进也是原样保留的;带星号的版本更进一步将空格显示为␣
。使用示例如下
1
2
3
4
5
6
7
8\begin{verbatim}
#include <iostream>
int main()
{
std::cout << "Hello, world!" << std::endl;
return 0;
}
\end{verbatim}
注意这不是代码环境,因为它只是原样输出,并没有提供编程语言的语法高亮功能。
还有与之类似的行内抄录片段或关键字的\verb
命令
1
\verb<delim><code><delim>
与常见的命令不同,\verb
需要提供自定义的分界符delim来标明代码的前后分界位置,要求前后分界符必须一致,除字母、空格或星号外,
可任意选择使得不与代码本身冲突的符号(习惯上使用|
符号),例如
1
2
3
4
5\verb+(a || b)+ %(a || b)
\verb|a\ b| % a\ b
\verb+\ldots+ % \ldots
代码环境 listings
listings宏包是一个功能强大也非常复杂的宏包,用于提供特定编程语言的语法高亮,但是默认的显示字体很丑,排版也很丑,需要经过一番配置才能达到美观的语法高亮显示效果。
1
\usepackage{listings}
通常需要搭配提供颜色的xcolor宏包使用(xcolor是color宏包的增强版)
1
\usepackage{xcolor}
可以定义新的颜色 1
2
3
4
5\definecolor{dkgreen}{rgb}{0,0.6,0}
\definecolor{gray}{rgb}{0.5,0.5,0.5}
\definecolor{mauve}{rgb}{0.58,0,0.82}
\definecolor{epubblue}{RGB}{1,126,218}
颜色的使用例如\color{gray}
。注意上面的小写rgb接受的参数为0-1的小数值,而大写RGB接受的是0-256的整数值。
基本使用
行内代码片段展示:\lstinline|<codes>|
,与\verb
命令类似,它的分界符可以是任意的,从而避免与代码中的字符冲突,例如
1
2
3\lstinline!var i:integer;!
\lstinline|int x=1;|
主要用法是导入代码块 1
2
3\begin{lstlisting}
<codes>
\end{lstlisting}
也支持从文件中导入代码块(可以设置起止行号来导入源文件中的片段)
1
2\lstinputlisting[language=Python]{main.py}
\lstinputlisting[language=Python, firstline=2, lastline=12]{main.py}
\lstlistoflistings
命令会将所有添加caption的代码块生成代码目录。
配置
配置既可以在导入特定环境时指定,也可以在导言区进行全局默认设置,在具体环境中以可选参数的形式给出配置,例如
1
2
3
4
5
6
7
8
9
10
11\begin{lstlisting}[
language=Python, % 语言
numbers=left, % 行号显示位置
firstnumber=10 % 行号
]
# Python
def hello():
print("Hello, world!")
hello()
\end{lstlisting}
或者可以在导言区进行全局默认配置,例如 1
2
3
4
5
6
7
8
9
10
11
12
13\lstset{
language=Python % 设置语言
columns = fixed, % 列距排版
basicstyle = \linespread{0.8} \ttfamily, % 设置行距,字体
numbers = left, % 行号显示设置
numberstyle = \tiny \color{gray}, % 设定行号格式
keywordstyle = \bfseries \color[RGB]{40,40,255}, % 关键字格式
commentstyle = \color[RGB]{0,96,96}, % 代码注释格式
stringstyle = \color[RGB]{128,0,0}, % 字符串格式
frame = single, % 背景边框
backgroundcolor = \color[RGB]{245,245,244}, % 背景颜色
showstringspaces = false, % 字符串中的空格显示
}
例如 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24\lstset{
language=C, % 代码语言
basicstyle=\footnotesize, % 代码字体大小
numbers=left, % 行号位置
numberstyle=\tiny\color{gray}, % 行号样式
stepnumber=2, % 行号步进
numbersep=5pt, % 行号与代码之间的距离
backgroundcolor=\color{white}, % 背景颜色
showspaces=false, % 是否显示空格,用特殊符号替代
showstringspaces=false, % 字符串中的空格是否用下划线标出
showtabs=false, % 是否显示制表符,用特殊符号替代
frame=single, % 为代码块添加单线框架
rulecolor=\color{black}, % 框架颜色
tabsize=2, % 制表符宽度
captionpos=b, % 标题位置(底部)
breaklines=true, % 自动换行
breakatwhitespace=false, % 仅在空格处自动换行
title=\lstname, % 显示包含代码的文件名
keywordstyle=\color{blue}, % 关键字样式
commentstyle=\color{dkgreen}, % 注释样式
stringstyle=\color{mauve}, % 字符串样式
escapeinside={\%*}{*)}, % 在代码中支持 LaTeX 命令
morekeywords={*,...} % 添加更多关键字
}
其中常见的选项:
- 语言设置:
language=Python,C++
,支持多个语言,注意名称要用C++而非Cpp - 基本样式设置
basicstyle = \ttfamliy
,这是指定等宽字体(建议) - 样式:主要是颜色和字体设置,针对关键词,字符串和注释
- 关键字样式:
keywordstyle=\bfseries \color[RGB]{40,40,255}
,通常指定一个颜色 - 注释样式:
commentstyle=\color[RGB]{0,96,96}
, 通常指定一个颜色 - 字符串样式:
stringstyle=\color[RGB]{128,0,0}
, 通常指定一个颜色 - 行号样式:
numberstyle=\footnotesize
,这可以让行号的字体更小一点 - 标识符样式:
identifierstyle=\color{black}
,标识符的颜色字体等
- 关键字样式:
- 补充关键词:
morekeywords={eg1,eg2}
,这可以加入自定义关键词,默认的不太全 - 自动换行:
breaklines=true
,建议,否则过长的行会直接截断! - 行号显示:
- 是否显示行号:
numbers=left
,支持left/right/none,默认为none不显示 - 间隔多少行显示行号:
stepnumber=5
- 行号起始:
firstnumber=10
,设置为last则继承上一个代码环境的行号
- 是否显示行号:
- 列间距设置:
columns=flexible
,让不同列之间的距离自适应,反之可以使用fixed
设置固定距离 - 字符串空格显示:
showstringspaces=false
,否则默认会在某些空格处显示下划线 - tab与缩进:
- 突出显示tab:
showtabs=true
,会将tab显示为长的下划线,默认不显示(只展示连续空格) - tab长度:
tabsize=8
,默认长度为8个空格
- 突出显示tab:
- 边框:
frame=single
,single显示单边框,shadowbox为阴影框,默认是none无边框 - 背景颜色:
backgroundcolor=xxx
- 代码块标记:
name=xxx
,同名的代码块行号会自动延续 - 代码块显示名称:
caption=Name
,还可以指定名称的显示位置(默认在顶部),例如captionpos=b
改在底部,caption会自动编号,如果不需要编号,可以使用title=xxx
,如果同时出现则会显示后设置的项 - 忽略部分字符以支持latex命令:
escapeinside={\%*}{*)}
除此之外,还可以定义一套新的样式(而非设置默认样式)用于指定语言,例如
1
2
3\lstdefinestyle{cppStyle}{
...
}
使用例如 1
2
3
4
5
6
7
8\begin{lstlisting}[style=cppStyle]
#include <iostream>
int main() {
std::cout << "Hello, world!" << std::endl;
return 0;
}
\end{lstlisting}
提供一个默认的配置吧,看着还行 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22\lstdefinestyle{mystyle}{
basicstyle=\small\ttfamily,
commentstyle=\color[RGB]{34,139,34},
keywordstyle=\color[RGB]{0,0,255},
numberstyle=\tiny\color{gray},
stringstyle=\color[RGB]{128,0,128},
identifierstyle=\color{black},
showstringspaces=false,
tabsize=4,
breaklines=true,
numbers=left,
frame=single,
%backgroundcolor=\color[RGB]{242,242,242},
rulecolor=\color{black},
captionpos=b,
xleftmargin=\parindent,
aboveskip=\baselineskip,
belowskip=\baselineskip,
escapeinside={\%*}{*)},
}
\lstset{style=mystyle}
还有MATLAB的配置参考 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21\definecolor{matlablilas}{RGB}{170,55,241}
\lstset{
language=Matlab,%
basicstyle=\ttfamily, % 设置字体族
breaklines=true,%
morekeywords={},
keywordstyle=\color{blue},%
morekeywords=[2]{1}, keywordstyle=[2]{\color{black}},
identifierstyle=\color{black},%
stringstyle=\color{violet},
commentstyle=\color{teal},%
showstringspaces=false,%without this there will be a symbol in the places where there is a space
emph=[1]{for,end,break},emphstyle=[1]\color{red}, %some words to emphasise
%emph=[2]{word1,word2}, emphstyle=[2]{style},
% columns=flexible,
numbers=left, % 显示行号在左边
numbersep=2em, % 设置行号的具体位置
numberstyle=\footnotesize, % 缩小行号
frame=single, % 边框
framesep=1em % 设置代码与边框的距离
}
除了listings之外,minted宏包是另一种常见语法高亮方案,基于Python提供的支持进行语法高亮,因此在编译时需要传入特殊参数允许执行外部命令(调用Python),暂略。