使用或不使用宏对逗号分隔列表进行排序

使用或不使用宏对逗号分隔列表进行排序

我希望能够对以逗号分隔的列表进行排序。

在我写一些需要\ensureunix:-) 的东西并让它帮我完成排序之前,我想尝试在 TeX 中完成它。因此,在参考文献中列出的两个问题的帮助下,我能够让它工作,但是仅有的对于列表的情况不是在宏中定义。不幸的是,我只需要它起作用的情况宏定义列表,但如果有一个版本可以同时处理这两个问题就更好了。

  1. 注释掉%\def\SupportMacroDefinedList{}后(如给定的 MWE 所示),您可以看到,当列表未在宏中定义时(图像右侧),排序工作正常。没有虚假空格,空列表处理得很好:

    在此处输入图片描述

  2. 对于宏定义列表(即\def\SupportMacroDefinedList{}未注释),这是我能做的最好的:

    在此处输入图片描述

    这被标记为“几乎”工作有几个原因,请注意,每种情况的列表宏定义都不同。其中一些问题是:

    • %第一个条目后需要尾随。没有它,排序顺序就会改变!!
    • 它不能正确处理空列表(上面的非宏版本没有问题)。
    • 多个连续的逗号被正确地处理为非列表成员,如Zebra,,,但如果逗号之间有空格,如 ,Zebra, ,则会产生错误Undefined control sequence. <argument> \@xfor@endmarker
    • 类似地,不能只有逗号的一行(可能与多个连续逗号中的空格存在同样的问题)。
    • 这需要%在最后一个列表成员后面添加一个尾随。尝试将其删除,或在之前添加一个空格%,并理解排序顺序。

笔记:

  • 输出中的逗号仅打印在每个列表成员的末尾,以便查看是否存在任何虚假空格。
  • 根据如何处理以逗号分隔的列表?\clist_map_inline:nn应该能够去除虚假空格,但对我来说似乎不行。相反,它将宏定义列表视为单个成员列表。此外,这个相同的链接问题引用了但这对我来说\clist_map_inline:on会产生编译时错误: 。Undefined control sequence

参考:

最低要求:

为了对我有用,我真的只需要它来处理

  1. 空列表(仅逗号、空格和/或换行符),

  2. ,最后一个列表成员无尾随:

    \newcommand*{\ListMembers}{%
      Odd,
      Zebra,
      Even,
      Alpha
    }%
    
  3. ,最后一个成员尾随:

    \newcommand*{\ListMembers}{%
      Odd,
      Zebra,
      Even,
      Alpha,
    }%
    

不使用expl3语法的东西也很好,因为这样我至少可以在需要的时候修改它。:-)

代码:

%\def\SupportMacroDefinedList{}%

\documentclass{article}
\usepackage{datatool}
\usepackage{xparse}
\usepackage{xstring}

\newcommand*{\SortDBName}{sortDB}%
\newcommand*{\DBKey}{label}%
\newcommand{\TitleA}{\textit{My Enumerated List:}}%
\newcommand{\TitleB}{\textit{My Empty List:}}%


%------------------------------------- Sort CSV list
% Adapted from:
%    https://tex.stackexchange.com/questions/40031/how-to-process-a-comma-separated-list
%    https://tex.stackexchange.com/questions/6988/how-to-sort-an-alphanumeric-list

\newcommand{\SortItem}[1]{%
    \IfStrEq{#1}{\empty}{%
        % Skip empty list members
    }{%
        \DTLnewrow{\SortDBName}%
        \IfEndWith{#1}{\space}{% attempt to remove any trailing space
            \DTLnewdbentry{\SortDBName}{\DBKey}{\StrGobbleRight{#1}{1}}%
        }{%
            \DTLnewdbentry{\SortDBName}{\DBKey}{#1}%
        }%
    }%
}

% How do I make \SortCommaSeparatedList work for BOTH cases 
\ifdefined\SupportMacroDefinedList%
    \ExplSyntaxOn
    \NewDocumentCommand{\SortCommaSeparatedList}{>{\SplitList {,}}m}
      { \clist_map_inline:Nn {#1} { \SortItem {##1} } }% List in macro
    % Note: nn is supposed to handle spurious spaces as per ... but doesn't
    \ExplSyntaxOff
\else
    \ExplSyntaxOn
    \NewDocumentCommand{\SortCommaSeparatedList}{>{\SplitList {,}}m}
     { \tl_map_inline:nn    {#1} { \SortItem {##1} } }% Non macro list
    \ExplSyntaxOff
\fi%



\newcommand{\SortedList}[3]{%
    % #1 = type of list 
    % #2 = title to print
    % #3 = list content (Comma separated list)
    %
    \noindent#2\par%
    %
    \DTLifdbexists{\SortDBName}%
        {\DTLcleardb{\SortDBName}}% DB exists, so just clear it
        {\DTLnewdb{\SortDBName}}%   DB does not exist, so create it
    %
    \SortCommaSeparatedList{#3}%
    \DTLsort{\DBKey}{\SortDBName}%
    \IfEq{\DTLrowcount{\SortDBName}}{0}{%
        \par%
        No items to print for ``#1" list.%
    }{%
        \begin{#1}%
            \DTLforeach*{\SortDBName}{\CurrentItem=\DBKey}{%
                \item \CurrentItem,%
        }%
        \end{#1}%
    }%
}%

\ifdefined\SupportMacroDefinedList%
    \newcommand*{\ListMembers}{% Why is % required after the first line?
        Odd,%
        Zebra,
        %,%  Can't have this
        Even,
        Alpha%
    }%
    \newcommand*{\EmptyListMembers}{%
     , ,% %%% Note: Spaces after last comma NOT-ok for macro version
    }%
\else%
    \newcommand*{\ListMembers}{%
        Odd,, 
        Zebra, ,
        ,
        Even,    
        Alpha  ,
    }%
    \newcommand*{\EmptyListMembers}{%
     , ,  % %%% Note: Spaces after last comma ok for non-macro version
    }%
\fi%


\begin{document}
\ifdefined\SupportMacroDefinedList%
    \section*{Almost works for Macro Defined List}%
\else%
    \section*{Works for non-macro Defined List}%
\fi%

\begin{minipage}[t]{0.45\linewidth}\noindent%
    \textbf{Macro defined list}\medskip\par%
    \SortedList{enumerate}{\TitleA}{\ListMembers}
    \SortedList{itemize}{\TitleB}{\EmptyListMembers}
\end{minipage}%
%
\hfill
%
\begin{minipage}[t]{0.45\linewidth}%
    \textbf{Non-macro defined list}\medskip\par\noindent%
    \SortedList{enumerate}{\TitleA}{  Odd, Zebra, , Even , Alpha  ,}
    \SortedList{itemize}{\TitleB}{ , , }
\end{minipage}%
\end{document}

答案1

解决这个问题的方法有很多:选择哪种方法取决于您的具体要求。

以问题中的例子为例,显然失败的原因在于\clist_map_inline:nn非常expl3小心,不会“意外”扩展任何东西。因此,当抓取的参数是宏时包含逗号分隔的列表,代码永远不会看到逗号:您知道,这可能是一个只有一个项目的列表,而这个项目恰好是一个包含另一个列表的宏!expl3处理“存储”和“内联”逗号列表的函数之间也存在差异。本质上,这个想法是“存储”列表已经经过清理,删除了空格和空项目。所以你需要做的是使用排队列出函数并扩展您的输入一次:

\NewDocumentCommand \SortCommaSeparatedList { m }
  { \exp_args:No \clist_map_function:nN {#1} \SortItem }

这将适用于您输入的两种形式,因为您的“内联”列表仅包含不可扩展的标记。一般来说,您不能假设这一点,所以我认为应该将其\SortCommaSeparatedList描述为接受包含列表的宏,或接受列表,但不能同时接受两者。

为了避免expl3,也许最简单的方法是使用 LaTeX2e\@for以及一些空格剥离代码,同样尽可能少地进行更改:

\makeatletter
\newcommand*{\SortCommaSeparatedList}[1]{%
  \expandafter\@for\expandafter\@tempa\expandafter:\expandafter=#1\do{%
    \edef\@tempa{\expandafter\trim@spaces\expandafter{\@tempa}}%
    \expandafter\SortItem\expandafter{\@tempa}%
  }
}

% This is expl3's \tl_trim_spaces:n
\def\@tempa#1{%
  \newcommand{\trim@spaces}[1]{%
    \unexpanded\trim@spaces@aux@i\@mark##1\@nil\@mark#1{}\@mark
      \trim@spaces@aux@ii\trim@spaces@aux@iii#1\@nil\trim@spaces@aux@iv\@stop
  }
  \newcommand{\trim@spaces@aux@i}{}
  \long\def\trim@spaces@aux@i##1\@mark#1##2\@mark##3{%
    ##3%
    \trim@spaces@aux@i\@mark##2\@mark#1{##1}%
  }
  \newcommand{\trim@spaces@aux@ii}{}
  \long\def\trim@spaces@aux@ii##1\@mark\@mark##2{%
    \trim@spaces@aux@iii##2%
  }
  \newcommand{\trim@spaces@aux@iii}{}
  \long\def\trim@spaces@aux@iii##1#1\@nil##2{%
    ##2%
    ##1\@nil
    \trim@spaces@aux@iii
  }
  \newcommand{\trim@spaces@aux@iv}{}
  \long\def\trim@spaces@aux@iv##1\@nil##2\@stop{%
    \expandafter{\@gobble##1}%
  }
}
\@tempa{ }
\makeatother

这再次扩展了参数一次,这次我们对每个项目进行更尴尬的循环。空间修剪代码与 中的代码完全相同expl3,但以更“传统”的形式编写。(您可以使用 中的代码更有效地编写循环expl3,但这似乎需要付出更多努力,而实际收获却很少。)

您可以通过几种方式进一步了解。首先,如果您愿意坚持使用,expl3那么您可以避免加载xstring并使用等等进行比较\tl_if_empty:nTF。还有一个实验性的排序模块可以为您完成整个工作!另一方面,由于expl3需要\pdfstrcmp原语,您可以使用它进行排序,尽管这会稍微复杂一些,因为它纯粹基于字符代码。最后,当然您可以使用 LuaTeX,并在 Lua 中进行排序(我猜您想要一些相当通用的,所以这可能不行)。

相关内容