在 LaTeX 中写排序宏命令对 TOKEN 排序
不管你信不信, LaTeX 的宏系统是可以用来排序的。这一篇博文会讲述如何在 LaTeX 里面来进行对 token
的排序。
简介
起因是想在一个论文中对交叉引用的某个东西排序。然后就通过搜索引擎找到了这个在 Stack Exchange 上的 回答。最后根据这个回答改了我宏包中的一些代码,使得交叉引用某个东西的时候,会根据序号进行排序。
知识点
这个回答中对于排序的设计完全是基于 LaTeX/TeX 宏系统的。但是我目前对于这个宏系统的了解也不多。尽管如此,我还是会尽量解释清楚的。
最重要的是类似递归的思想,毕竟是宏系统,通过类似递归的思想,让待排序的 Token 在展开的时候在不同的“队列”中“移动”,然后反复比较一遍遍挑出最小的(或者最大的,这个看根据比较函数确定)。
原理分析
首先先贴一下回答中作者贴上去的部分代码(排序的核心部分)。如果实在看不懂的话,先看我的总结部分的内容,然后再看我的分析,可能就清除一下了。
1 | % #1 - comparator |
入口 \sort
首先是入口函数 \sort
,入口函数接收两个参数: 一个是“比较”的宏命令,另一个是待排序的 token “们”。然后输入的参数被传送到了另一个 宏命令 \sort@begin
中。
要说明的是 \sort@begin
接收 “5” 个 token 作为输入分别是输入参数 #1
,空的 token {}
,输入参数 #2
,自定义的“终止” token \sort@s
,与 \sort@begin
宏命令自己。
Token \sort@begin
\sort@begin
宏命令只做了两件事情,判断了一下第四个输入的参数是不是 \sort@s
,宏命令 \ifsort@s
就是用来判断的。
如果第四个参数是 \sort@s
的时候,就意味着排序已经执行完了,会执行 \sortend
与 \sort
来“收尾”。但是实际上来说 再次展开 \sort
是开启了对没有排序的内容进行处理,但是你懂的。而\sortend
是一个自定义的宏命令,只是用来处理排序的结果的,直接写成 #3
也是可以的。
如果第四个参数不是 \sort@s
的话,则是会去展开 \sort@go
宏命令。
Token \sort@go
这个 Token 是实际负责作比较的, \sort@go
Token一共接收三个,不,准确是六个输入的的 token。第一个输入的 Token是负责比较的。第二个 Token 是可以理解为一种列表,是所有处理过的要排序 Token。
负责比较的 Token 定义应该是类似 \sort@numcmp
定义的。这个 Token 要负责解决真正的比较过程(比较 #3
与 #4
这两个输入) ,然后处理 #3
、#4
与 {\sort@output#1{#2}{#5}}
这三个 Token 之间的关系:是展开 \sort@output#1{#2}{#5{#3}{#4}
还是展开 \sort@output#1{#2}{#5}{#4}{#3}
。
Token \sort@output
这个 Token 是负责输出的,因为 LaTeX 本身是一种宏系统,排版用的,所以这个代码就是为了解决输出内容排版的,所以就是为了输出。
输出的条件是 #3
是否是 Token \sort@s
,如果是的话,那就意味着一次扫描已经完成了,从所有要排序的 Token 中 找出最小的(或者是最大的,这个比较由 #1
执行),然后继续在已经处理过的 Token 中找出下一个最小的。
如果不是 Token \sort@s
,那就继续比较后面的没有比较的 Token。
总结
如果你看完了分析,尤其是 Token \sort@output
的,你是不是觉得有一种似曾相识的感觉??对!这个算法就是利用 LaTeX 宏系统整出来的选择排序。 从 \sort@begin
到 \sort@output
的反复的过程是在所有待排序的 Token 中找出来最小的一个,然后输出。剩下的 Token 就继续传递给 \sort
从头再来一遍排序。
Reference
[1] programming - How to sort an alphanumeric list - TeX - LaTeX Stack Exchange. Stackexchange. Retrieved July 30, 2020 from https://tex.stackexchange.com/questions/6988/how-to-sort-an-alphanumeric-list/7095#7095
我最后在论文中使用的代码
下面是我在论文中最终使用的代码。和原始的代码相比,除了 Token 的名字不同还没注释外,我这个代码还有一个特别之处,我的待排序的 token 是用逗号分割的 label,然后比较的结果是按照 label 对应的计数器的值排序的。
1 | \RequirePackage{etoolbox} |