制作自定义队列容器时宏扩展的问题

制作自定义队列容器时宏扩展的问题

我正在尝试etoolbox通过创建自己的自定义宏\listfront和来使用该包创建一个简单的队列\listfrontpop。这是我第一次将 TeX 代码使用到这种程度,所以我的理解充其量只是浅薄的。以下是我目前所拥有的:

\documentclass{minimal}
\usepackage{etoolbox}

\newcommand{\listfront}[1]{%
  \renewcommand{\do}[1]{##1\listbreak}%
  \dolistloop{#1}%
}

\newcommand{\listpopfront}[1]{%
  \def\templistfront{\listfront{#1}}%
  \listgremove{#1}{\templistfront}%
}

\begin{document}
  \newcommand{\mylist}{}%
  \listgadd{\mylist}{potty}%
  \listgadd{\mylist}{sam}%
  \listfront{\mylist}%       potty   <- as expected
  \listpopfront{\mylist}%    potty should be removed
  \listfront{\mylist}%       potty   <- expected sam!
\end{document}

我期望得到pottysam,但得到的pottypotty却是 。我怀疑问题在于

...
\begin{document}
...
  \listpopfront{\mylist}%
...
\end{document}

因为如果我将其替换为\listgremove{\mylist}{potty},我会得到pottysam。但是,如果我执行类似操作\def\potty{potty},然后执行\listgremove{\mylist}{\potty},我会pottypotty再次得到。

我该怎么去\listpopfront上班?出了什么问题?

到目前为止,我的解决方案是\listpopfront定义为

\newcommand{\listpopfront}[1]{%
  \renewcommand{\do}[1]{%
    \listgremove{#1}{##1}\listbreak%
  }%
  \dolistloop{#1}%
}

这解决了我的问题,但是却非常不优雅。

答案1

这是一个解决方案,它只使用 etoolbox 的文档,而不使用其内部组件,并且是从您的 MWE 开发而来的。我允许自己使用它,\def因为您已经使用过它了。

\documentclass{article}
\usepackage{etoolbox}

\newcommand{\listfront}[1]{%
  \renewcommand{\do}[1]{##1\listbreak}%
  \dolistloop{#1}%
}

\newcommand{\listpopfront}[1]{%
  \begingroup
  \long\def\do##1{\long\def\templistfront{##1}\listbreak}%
  \dolistloop{#1}%
  \def\x{\endgroup\listgremove{#1}}%
  \expandafter\x\expandafter{\templistfront}%
}

\begin{document}
  \newcommand{\mylist}{}%
  \listgadd{\mylist}{potty}%
  \listgadd{\mylist}{sam}%
  \listfront{\mylist}%       potty   <- as expected
  \listpopfront{\mylist}%    potty should be removed
  \listfront{\mylist}%       sam     <- as expected
\end{document}

在此处输入图片描述

我很惊讶 etoolbox 没有为此目的提供“pop”,因为使用\listgremove看起来可能非常低效,但我检查了宏跟踪,我发现它基于使用\ifinlist分隔宏,而不是逐项检查,此外它只删除第一个找到的项目(文档对此并不清楚,但我从检查扩展中得出结论)。

请注意,LaTeX 内核已经有内置工具\@car\@cdr\@tfor(更不用说 LaTeX3 现在合并的层......)您可以使用它来构建自己的,但我想etoolbox如果您打算调用它的话,使用它来说是合乎逻辑的\dolistloop

这是现在使用内部 etoolbox 结构的代码。幸运的是,它非常简单明了,但如果将来发生变化,此代码将被破坏。

\documentclass{article}
\usepackage{etoolbox}

% this command is only good for typesetting,
% it does not work expandably
\newcommand{\listfront}[1]{%
  \renewcommand{\do}[1]{##1\listbreak}%
  \dolistloop{#1}%
}

\makeatletter
\begingroup
% definition of a "popfront" as per OP command name
% it removes the first item globally
\catcode`\|=3 % internal separator used by etoolbox
\gdef\listpopfront#1{%
  \expandafter\listpopfront@i#1\@nil{#1}%
}
\long\gdef\listpopfront@i#1|#2\@nil#3{\long\gdef#3{#2}}

% we also define for the fun an expandable \listexpandablefront
% now that we are using internals of etoolbox
\gdef\listexpandablefront#1{%
  \expandafter\listexpandablefront@i#1\@nil
}
\long\gdef\listexpandablefront@i#1|#2\@nil{#1}
\endgroup
\makeatother

\begin{document}
  \newcommand{\mylist}{}%
  \listgadd{\mylist}{potty}%
  \listgadd{\mylist}{sam}%
% \show\mylist
  \listfront{\mylist}%       potty   <- as expected
  \listpopfront{\mylist}%    potty should be removed
% \show\mylist
  \listfront{\mylist}%       sam    <- as expected

  \edef\foo{\listexpandablefront{\mylist}}
  \texttt{\meaning\foo}
\end{document}

具有宏含义的屏幕截图

答案2

您似乎想要 FIFO 列表。我会使用expl3而不是etoolbox;请注意,列表名称没有反斜杠,这避免了选择命令名称的问题。

\documentclass{article}

\ExplSyntaxOn

%%% the user level commands

% declare a list and optionally add items
\NewDocumentCommand{\declarelist}{mo}
 {
  \daedsidog_list_declare:n { #1 }
  \IfValueT{#2}
   {
    \daedsidog_list_gadd:nn { #1 } { #2 }
   }
 }
% add items to a list
\NewDocumentCommand{\listgadd}{mm}
 {
  \daedsidog_list_gadd:nn { #1 } { #2 }
 }
% deliver the front item (this is expandable)
\NewExpandableDocumentCommand{\listfront}{m}
 {
  \daedsidog_list_front:n { #1 }
 }
% pop the front item
\NewDocumentCommand{\listpopfront}{m}
 {
  \daedsidog_list_popfront:n { #1 }
 }

%%% the internal functions

% declare a list
\cs_new_protected:Nn \daedsidog_list_declare:n
 {
  \seq_new:c { g__daedsidog_list_#1_seq }
 }
% add items to a list
\cs_new_protected:Nn \daedsidog_list_gadd:nn
 {
  \clist_map_inline:nn { #2 }
   {
    \seq_gput_right:cn { g__daedsidog_list_#1_seq } { ##1 }
   }
 }
% deliver the front item
\cs_new:Nn \daedsidog_list_front:n
 {
  \seq_item:cn { g__daedsidog_list_#1_seq } { 1 }
 }
% pop the front item
\cs_new_protected:Nn \daedsidog_list_popfront:n
 {
  \seq_gpop_left:cN { g__daedsidog_list_#1_seq } \l_tmpa_tl
 }

\ExplSyntaxOff

\declarelist{mylist}[potty,sam]

% could also be
%\declarelist{mylist}
%\listgadd{potty,sam}
% or
%\declarelist{mylist}
%\listgadd{potty}
%\listgadd{sam}

\begin{document}

\listfront{mylist}%       potty   <- as expected
\listpopfront{mylist}%    potty should be removed
\listfront{mylist}%       potty   <- expected sam!

\end{document}

您可以一次添加多个项目(声明列表时也可以)。

在此处输入图片描述

相关内容