这有点不寻常。但是有人用过 LaTeX 来压缩数字列表吗?
我想到的最接近的例子是使用引用包。因此,如果我写\cite{key1, key2, key3, key4, key5}
,我们会得到类似 1-3、7、9 的内容。
所考虑的情况几乎相似,只是数字是实数或扩展为实数,而不是引用键。因此,如果我写\compactthis{1,2,3,4,5,9,8,7, 11}
,我将得到 1-5、7-9、11。
有没有快速实现此目的的方法?从头开始编写宏需要花费时间,而我现在无法承受。
(我知道编程语言更适合这种情况,但我目前的情况要求使用 LaTeX。)
答案1
编辑以对输入流进行排序使用冒泡排序(如果需要,\bubblesort
可以独立于\compactthis
宏使用宏)。
必须\listterminator
设置为任何已知不在列表中的数值(例如,足够大、负数、零等),当前设置为9999
。如图所示(在代码中和在 MWE 中演示),如果对于相邻的限制条目,优先于 ,\adjtie
则可以将宏设置为。 --
1--2
1, 2
无需任何包裹。
\documentclass{article}
%
% THIS CODE CAN \bubblesort A NUMBERED LIST AND THEN \compactthis LIST IN THE MANNER
% OF 1-3, 7, 11-13
%
\def\listterminator{9999}% SET TO *ANY* VALUE KNOWN NOT TO BE IN LIST (POSITIVE OR NEGATIVE)
\def\adjtie{, }
%\def\adjtie{--}% OPTIONAL IF 1--2 preferred over 1, 2
\newcommand\compactthis[1]{%
\bubblesort{#1}%
\expandafter\begincompaction\sortedlist,\listterminator,\relax%
}
\def\begincompaction#1,#2\relax{%
\def\startlist{#1}%
\def\currentendlist{#1}%
\findendlist#2\relax%
}
\def\findendlist#1,#2\relax{%
\ifnum\numexpr\currentendlist+1\relax=#1\relax%
\def\currentendlist{#1}%
\findendlist#2\relax%
\else%
\ifnum\startlist=\currentendlist\relax%
\ignorespaces\startlist\unskip%
\else%
\ifnum\numexpr\startlist+1\relax=\currentendlist\relax%
\ignorespaces\startlist\unskip\adjtie\ignorespaces\currentendlist\unskip%
\else%
\ignorespaces\startlist\unskip--\ignorespaces\currentendlist\unskip%
\fi%
\fi%
\ifnum#1=\listterminator\else,\ \begincompaction#1,#2\relax\fi%
\fi%
}
\newcommand\bubblesort[1]{\def\sortedlist{}\sortlist#1,\listterminator,\relax}
\def\sortlist#1,#2,#3\relax{%
\ifnum#2=\listterminator\relax%
\edef\sortedlist{\sortedlist#1}%
\else
\ifnum#1<#2\relax%
\edef\sortedlist{\sortedlist#1,}%
\sortlist#2,#3\relax%
\else%
\let\tmp\sortedlist%
\def\sortedlist{}%
\expandafter\sortlist\tmp#2,#1,#3\relax%
\fi%
\fi%
}
\begin{document}
Bubble Sort Demonstration:
\bubblesort{1,2,11, 7, 4, 3}\sortedlist\par
\compactthis{1,2,3,4,5,7,8,9, 11}\par
\compactthis{1,2, 12 ,13 ,18, 20} (single member)\par
\compactthis{1,2, 12,13,18, 19, 20} (range at end)\par
\def\adjtie{--}\compactthis{1,2, 12,13,18, 19, 20} (\verb|\adjtie| set to {--})\par
\compactthis{1,2,11, 7, 4, 3, 12, 14, 13} (unsorted input)
\end{document}
答案2
以下是您需要的大部分内容,其中输入为黑色,输出为红色:
笔记:
- 这里缺少的主要内容是对初始列表进行排序。这个网站上有很多解决方案,但我一直在使用的一个是使用或不使用宏对逗号分隔列表进行排序. 另外一个是:如何对字母数字列表进行排序
- 对于未经测试的情况,这也需要进一步测试。
代码:
\documentclass{article}
\usepackage{pgffor}
\usepackage{xstring}
\usepackage{etoolbox}
\usepackage{xcolor}
\newtoggle{StartedRange}
\newcommand{\LastNumber}{}%
\newcommand{\LastRangeStart}{}%
\newcommand*{\compactthis}[1]{%
\edef\ExpandedParam{#1}% <-- Apply sorting here
\ExpandedParam:
\begingroup\color{red}%
\togglefalse{StartedRange}%
\foreach \x in \ExpandedParam {%
\iftoggle{StartedRange}{%
\pgfmathtruncatemacro\ExpectedNextNumber{\LastNumber+1}%
\IfEq{\ExpectedNextNumber}{\x}{%
%% Continue this range
}{%
\IfEq{\LastRangeStart}{\LastNumber}{%
%% Was a single member
}{%
--\LastNumber% Close last range
}%
,\, \x% and start a new range
\xdef\LastRangeStart{\x}%
}%
}{%
\x% initial range
\xdef\LastRangeStart{\x}%
\global\toggletrue{StartedRange}%
}%
\xdef\LastNumber{\x}%
}%
% Process any ranges at end of list:
\IfEq{\LastRangeStart}{\LastNumber}{%
%% Was a single member
}{%
--\LastNumber% Close last range
}%
\endgroup%
}%
\begin{document}
\compactthis{1,2,3,4,5,7,8,9, 11}
\compactthis{1,2, 12,13,18, 20}% Single member
\compactthis{1,2, 12,13,18, 19, 20}% range at end
\end{document}
答案3
这是一个 LuaLaTeX 解决方案。由于 Lua 具有易于使用的排序函数 ( table.sort
),因此它还会对输入列表进行排序。可能有一些方法可以使 Lua 代码更简洁,但对于熟悉命令式编程语言的人来说,它应该很容易理解。
\documentclass{article}
\usepackage{luacode}
\begin{luacode*}
function print_range(range_min, range_max)
if range_min == range_max then
tex.sprint(tostring(range_min))
else
tex.sprint(tostring(range_min) .. "--" .. tostring(range_max))
end
end
function compactthis(...)
local numbers = {...}
table.sort(numbers)
local range_started = false
local range_min = 0
local range_max = 0
for i = 1, #numbers do
if range_started then
if numbers[i] <= range_max + 1 then
range_max = numbers[i]
else
print_range(range_min, range_max)
range_started = false
end
end
if not range_started then
if i ~= 1 then
tex.sprint(", ")
end
range_started = true
range_min = numbers[i]
range_max = numbers[i]
end
end
if range_started then
print_range(range_min, range_max)
end
end
\end{luacode*}
\newcommand\compactthis[1]{\luaexec{compactthis(#1)}}
\begin{document}
\compactthis{1,2,3,4,5,9,8,7,11}
\end{document}