在 LaTeX 中,有没有一种简单的方法可以按字母顺序对逗号分隔的列表进行排序?我尝试\sortlist{World, Hello}
使用文档示例编写一个宏(→“Hello, World”)l3sort
,但完全失败了。
\ExplSyntaxOn
\def\@sortlist{}
\newcommand{\sortlist}[1]{
\clist_set:Nn \l_foo_clist {#1}
\clist_sort:Nn \l_foo_clist{
\int_compare:nNnTF { ##1 } > { ##2 }
{ \sort_return_swapped: }
{ \sort_return_same: }
}
\def\@sortlist{\l_foo_clist}
}
\ExplSyntaxOff
是否有类似\str_compare
进行字符串比较或l3sort
实际上只能用于数值的方法?
编辑:
如何处理以逗号分隔的列表?对我来说并没有什么帮助,或者说我无法解决我的问题。
答案1
这似乎有效,无需任何软件包。已编辑以解决大小写问题。
编辑:解决了在解决订单之前比较用尽字母时出现的问题,例如wash, washer
。
看附录用于(以某种方式)处理变音符号。
\documentclass{article}
\def\listterminator{;}
\makeatletter
\newcommand\alphabubblesort[1]{\def\presorted{}\def\sortedlist{}%
\sortlist#1,\listterminator,\relax}
\def\sortlist#1#2,#3#4,#5\relax{%
\if\listterminator#3#4\relax%
\edef\sortedlist{\sortedlist#1#2}%
\else
\ifnum\the\lccode`#1<\the\lccode`#3\relax%
\edef\sortedlist{\sortedlist\presorted#1#2, }%
\expandafter\def\expandafter\svfirst\expandafter{\presorted#3#4}%
\def\presorted{}%
\expandafter\sortlist\svfirst,#5\relax%
\else%
\ifnum`#1=`#3\relax%
\ifx\relax#2\relax%
\edef\sortedlist{\sortedlist\presorted#1#2, }%
\expandafter\def\expandafter\svfirst\expandafter{\presorted#3#4}%
\def\presorted{}%
\expandafter\sortlist\svfirst,#5\relax%
\else%
\ifx\relax#4\relax%
\edef\sortedlist{\sortedlist\presorted#3#4, }%
\expandafter\def\expandafter\svfirst\expandafter{\presorted#1#2}%
\def\presorted{}%
\expandafter\sortlist\svfirst,#5\relax%
\else
\g@addto@macro\presorted{#1}%
\sortlist#2,#4,#5\relax%
\fi%
\fi%
\else%
\let\tmp\sortedlist%
\def\sortedlist{}%
\expandafter\def\expandafter\svfirst\expandafter{\presorted#3#4}%
\expandafter\def\expandafter\svsecond\expandafter{\presorted#1#2}%
\def\presorted{}%
\expandafter\expandafter\expandafter\expandafter\expandafter%
\expandafter\expandafter\sortlist\expandafter\expandafter%
\expandafter\tmp\expandafter\svfirst\expandafter,\svsecond,#5\relax%
\fi%
\fi%
\fi%
}
\makeatother
\begin{document}
\alphabubblesort{book, boot, boat,toad,attic,wish,wash,wasn't,Cat ,Xylophone}
\sortedlist\par
\alphabubblesort{book, washer, boot, boat,toad,attic,wish,wash,wasn't,Cat ,Xylophone}
\sortedlist\par
\end{document}
附录
这是一个可以处理变音符号的版本,因为它们不会破坏算法。这是通过将\edef
上述算法中的 s 更改为适当扩展的\def
s 来实现的。
但是,这里的变音字母在排序时始终位于所有非变音字母之前。虽然这可能不是理想的行为,但仍然很有用。
\documentclass{article}
\def\listterminator{;}
\makeatletter
\newcommand\alphabubblesort[1]{\def\presorted{}\def\sortedlist{}%
\sortlist#1,\listterminator,\relax}
\def\sortlist#1#2,#3#4,#5\relax{%
\if\listterminator#3#4\relax%
\expandafter\def\expandafter\sortedlist\expandafter{\sortedlist#1#2}%
\else
\ifnum\the\lccode`#1<\the\lccode`#3\relax%
\expandafter\expandafter\expandafter\def\expandafter\expandafter%
\expandafter\sortedlist\expandafter\expandafter\expandafter{%
\expandafter\sortedlist\presorted#1#2, }%
\expandafter\def\expandafter\svfirst\expandafter{\presorted#3#4}%
\def\presorted{}%
\expandafter\sortlist\svfirst,#5\relax%
\else%
\ifnum`#1=`#3\relax%
\ifx\relax#2\relax%
\expandafter\expandafter\expandafter\def\expandafter\expandafter%
\expandafter\sortedlist\expandafter\expandafter\expandafter{%
\expandafter\sortedlist\presorted#1#2, }%
\expandafter\def\expandafter\svfirst\expandafter{\presorted#3#4}%
\def\presorted{}%
\expandafter\sortlist\svfirst,#5\relax%
\else%
\ifx\relax#4\relax%
\expandafter\expandafter\expandafter\def\expandafter\expandafter%
\expandafter\sortedlist\expandafter\expandafter\expandafter{%
\expandafter\sortedlist\presorted#3#4, }%
\expandafter\def\expandafter\svfirst\expandafter{\presorted#1#2}%
\def\presorted{}%
\expandafter\sortlist\svfirst,#5\relax%
\else
\g@addto@macro\presorted{#1}%
\sortlist#2,#4,#5\relax%
\fi%
\fi%
\else%
\let\tmp\sortedlist%
\def\sortedlist{}%
\expandafter\def\expandafter\svfirst\expandafter{\presorted#3#4}%
\expandafter\def\expandafter\svsecond\expandafter{\presorted#1#2}%
\def\presorted{}%
\expandafter\expandafter\expandafter\expandafter\expandafter%
\expandafter\expandafter\sortlist\expandafter\expandafter%
\expandafter\tmp\expandafter\svfirst\expandafter,\svsecond,#5\relax%
\fi%
\fi%
\fi%
}
\makeatother
\begin{document}
\alphabubblesort{book, washer, w\"asher, boot, boat,toad,attic,wish,wash,wasn't,
Cat, Xylophone, w\"ash, edifice, \'edifice, w\"asherei}
\sortedlist\par
\end{document}
该算法基于我的\bubblesort
宏:使用 LaTeX 压缩数字列表
答案2
Lua 怎么样?
\def\sortlist#1{%
\directlua{%
local t = { #1 }
table.sort(t)
tex.sprint(table.concat(t,", "))
}%
}
\sortlist{"World", "Hello"}
\bye
附录:这种方法的一个真正有用的优点是它是完全可扩展的,这意味着在
\edef\x{\sortlist{"World", "Hello"}}
该宏\x
将包含排序后的列表。
使用 ConTeXt MKIV 时,您可以使用内部设置解析器将逗号分隔列表转换为 Lua 表。这样做的好处是您无需自己处理引号。它还尊重内部逗号的分组。
\def\sortlist#1{%
\ctxlua{%
local t = utilities.parsers.settings_to_array("\luaescapestring{#1}")
table.sort(t)
tex.sprint(table.concat(t,", "))
}%
}
\starttext
\sortlist{World, Hello, {Entry, with, commas}, "Quotes"}
\stoptext
答案3
您可以安全地比较由 ASCII 字符组成的字符串;带有重音字符的单词将不起作用。
\documentclass{article}
\usepackage{expl3,xparse}
\ExplSyntaxOn
\prg_new_conditional:Nnn \john_string_if_before:nn { p,T,F,TF }
{% I hope the LaTeX3 police won't catch me
\int_compare:nTF { \tex_strcmp:D { #1 } { #2 } < 0 }
{
\prg_return_true:
}
{
\prg_return_false:
}
}
\NewDocumentCommand{\sortlist}{smm}
{
\IfBooleanTF{#1}
{
\clist_set:No \l__john_sortlist_data_clist { #2 }
}
{
\clist_set:Nn \l__john_sortlist_data_clist { #2 }
}
\john_sortlist:N \l__john_sortlist_data_clist
\clist_set_eq:NN #3 \l__john_sortlist_data_clist
}
\clist_new:N \l__john_sortlist_data_clist
\cs_new_protected:Nn \john_sortlist:N
{
\clist_sort:Nn #1
{
\john_string_if_before:nnTF { ##1 } { ##2 }
{
\sort_return_same:
}
{
\sort_return_swapped:
}
}
}
\ExplSyntaxOff
\begin{document}
\sortlist{World,Hello}{\mylistA}
\mylistA
\newcommand{\mylistB}{duck,cat,dog}
\sortlist*{\mylistB}{\mylistC}
\mylistB ${}\to{}$\mylistC
\end{document}
如果希望比较不区分大小写,请将定义更改\john_sortlist:N
为
\cs_new_protected:Nn \john_sortlist:N
{
\clist_sort:Nn #1
{
\john_string_if_before:nnTF { \str_foldcase:n {##1} } { \str_foldcase:n {##2} }
{
\sort_return_same:
}
{
\sort_return_swapped:
}
}
}
答案4
你可以尝试一下
这是接管https://tex.stackexchange.com/a/273084/4686(代码 5)
% ------------------------------------------------------------------
% Expandable routine to sort strings, based on a QuickSort algorithm
% and using \pdfstrcmp.
% ------------------------------------------------------------------
% USAGE
% -----
% if using XeTeX:
% \edef\mysortedlist {\QSfull{\mylist}}, or explicit \QSfull {foo, bar, zoo}
% if using PDFLaTeX: (and inputenc for accented letters)
% \begingroup
% \subdueutfviiienc
% \global\edef\mysortedlist {\QSfull{\mylist}}
% \endgroup
\documentclass{article}
\ifdefined\XeTeXinterchartoks
\let\pdfstrcmp\strcmp
\usepackage{fontspec}
\else
\usepackage[T1]{fontenc}
\usepackage[utf8]{inputenc}% for PDFLaTeX
\makeatletter
% this \subdueutfviiienc allows é, à, etc... to survive as is to \edef's.
\newcommand*\subdueutfviiienc {% to be used in a group
\count@="C2
\loop
\lccode`~\count@
\lowercase{\def~####1{\noexpand~\string####1}}%
\ifnum\count@<"E0
\advance\count@\@ne
\repeat
\loop
\lccode`~\count@
\lowercase{\def~####1####2{\noexpand~\string####1\string####2}}%
\ifnum\count@<"F0
\advance\count@\@ne
\repeat
\loop
\lccode`~\count@
\lowercase{\def~####1####2####3{\noexpand~\string####1\string####2\string####3}}%
\ifnum\count@<"F4
\advance\count@\@ne
\repeat
}\makeatother
% (if inputenc is used with 8bit encoding another approach would be needed)
\fi
% CODE TAKEN FROM https://tex.stackexchange.com/a/273084/4686 (code 5)
% -------------------------------------------------------------------
\makeatletter
\long\def\xintdothis #1#2\xintorthat #3{\fi #1}%
\let\xintorthat \@firstofone
%
% use some (improbable) tokens as delimiters
\catcode`! 3
\catcode`? 3
\catcode`; 3
%
% first we check if empty list (else \qsfull@finish will not find a comma)
% we apply f-expansion to the argument to allow it to be a macro.
%
\def\QSfull #1{\expandafter\qsfull@a\romannumeral-`0#1,!,?}%
%
% first check if input has only blanks, or is empty
\def\qsfull@a #1{\ifx,#1\xintdothis\qsfull@a\fi
\ifx!#1\xintdothis\qsfull@abort\fi
\xintorthat{\qsfull@start #1}}%
\def\qsfull@abort #1?{}%
%
\def\qsfull@start {\expandafter\qsfull@finish\romannumeral0\qsfull@b,}%
\def\qsfull@finish ,#1{#1}% remove initial ,<space>
\def\qsfull@b ,#1#2,#3{\ifx?#3\xintdothis\qsfull@emptylist\fi
\ifx!#3\xintdothis\qsfull@singleton\fi
\xintorthat \qsfull@separate@a {}{}#1#2;#3}%
\def\qsfull@emptylist #1?{}%
\def\qsfull@singleton #1#2#3;!,?{, #3}%
%
\def\qsfull@separate@a #1#2#3;#4#5,%
% first pass, remove blanks in passing.
% no need to be extra efficient for that.
{%
\ifx,#4\expandafter\qsfull@valueisblank\fi
\ifx!#4\expandafter\qsfull@separate@done\fi
\if1\pdfstrcmp{#4#5}{#3}%
\expandafter\qsfull@separate@a@appendtogreater
\else\expandafter\qsfull@separate@a@appendtosmaller
\fi
#4#5?{#1}{#2}#3;%
}%
\def\qsfull@valueisblank \ifx#1\fi,#2?#3#4#5;{\qsfull@separate@a {#3}{#4}#5;#2,}%
\def\qsfull@separate@a@appendtogreater #1?#2{\qsfull@separate@a {#2, #1}}%
%
\def\qsfull@separate@a@appendtosmaller #1?#2#3{\qsfull@separate@a {#2}{#3, #1}}%
%
\def\qsfull@separate@done\if#1\fi #2?#3#4#5;?%
{%
\qsfull@c #4,!,?, #5\qsfull@c #3,!,?%
}%
% Now that the first pass is done, there are no more blank items.
% In particular here.
\def\qsfull@c ,#1#2,#3{\ifx?#3\xintdothis\qsfull@emptylist\fi
\ifx!#3\xintdothis\qsfull@singleton\fi
\xintorthat \qsfull@separate {}{}#1#2;#3}%
%
\def\qsfull@separate #1#2#3;#4#5,% blanks have already been filtered out.
{%
\ifx!#4\expandafter\qsfull@separate@done\fi
\if1\pdfstrcmp{#4#5}{#3}%
\expandafter\qsfull@separate@a@appendtogreater
\else\expandafter\qsfull@separate@a@appendtosmaller
\fi
#4#5?{#1}{#2}#3;%
}%
\def\qsfull@separate@appendtogreater #1?#2{\qsfull@separate {#2, #1}}%
\def\qsfull@separate@appendtosmaller #1?#2#3{\qsfull@separate {#2}{#3, #1}}%
%
%
\catcode`! 12
\catcode`? 12
\catcode`; 12
\makeatother
\begin{document}
\def\mylist{book, washer, wäsher, boot, boat,toad,attic,wish,wash,wasn't,
Cat, Xylophone, wäsh, edifice, édifice, wäsherei}
% use either a macro \mylist or explicit list as \QSfull argument.
\ifdefined\XeTeXinterchartoks
\edef\mysortedlist{\QSfull{\mylist}}
\else
{\subdueutfviiienc\global\edef\mysortedlist{\QSfull{\mylist}}}%
\fi
\mylist\par
\medskip
becomes (notice how whitespaces are normalized in the process):
\medskip
\mysortedlist
\medskip
For some reason due to the comparison being done by \verb|\pdfstrcmp| the é
comes after a-z.
% check
\typeout{\meaning\mylist}
\typeout{\meaning\mysortedlist}
\end{document}
这里是日志输出,使用 PDFLaTeX:
macro:->book, washer, wäsher, boot, boat,toad,attic,wish,wash,wasn't, Cat, Xyl
ophone, wäsh, edifice, édifice, wäsherei
macro:->Cat, Xylophone, attic, boat, book, boot, edifice, toad, wash, washer, w
asn't, wish, wäsh, wäsher, wäsherei, édifice
并使用 XeLaTeX(其切割线条方式不同):
macro:->book, washer, wäsher, boot, boat,toad,attic,wish,wash,wasn't, Cat, Xylo
phone, wäsh, edifice, édifice, wäsherei
macro:->Cat, Xylophone, attic, boat, book, boot, edifice, toad, wash, washer, w
asn't, wish, wäsh, wäsher, wäsherei, édifice
控制台输出(再次是 XeLaTeX)仍然不同,线条没有被切断:
macro:->book, washer, wäsher, boot, boat,toad,attic,wish,wash,wasn't, Cat, Xylophone, wäsh, edifice, édifice, wäsherei
macro:->Cat, Xylophone, attic, boat, book, boot, edifice, toad, wash, washer, wasn't, wish, wäsh, wäsher, wäsherei, édifice