我正在尝试构建一个类似于该\author
命令的命令。它需要一个参数,实际上是一个用 分隔的参数列表\and
。该命令需要将另一个命令(如\underline
或 )\emph
应用于列表的每个元素。我目前的方法是
\documentclass{minimal}
\newcommand{\mylist}[1]{%
\def\and{\noexpand\endgroup \noexpand\underline\noexpand\begingroup}%
\edef\myinternallist{\noexpand\underline\noexpand\begingroup #1\noexpand\endgroup}%
\show\myinternallist%
\myinternallist%
}
\begin{document}
\mylist{Element1 \and Element2}
\end{document}
并\show\myinternallist
产生
> \myinternallist=macro:
->\underline \begingroup Element1 \endgroup \underline \begingroup Element2\endgroup .
在我这个外行人看来,这没什么问题。但是,上面的代码无法编译,我也不知道为什么无法编译。预期结果如下所示:
答案1
你确实得到了
\underline \begingroup Element1 \endgroup \underline \begingroup Element2\endgroup
但不幸的是\underline\begingroup Element1 \endgroup
这是非法的,因为\begingroup
和\endgroup
不能用于限定宏参数。
有各种各样的工具可以实现这一点;我将展示一个基于expl3
未来 LaTeX3 的编程层的解决方案。
\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\NewDocumentCommand{\mylist}{ O{\underline} m }
{
% transfer control to an internal function
\porst_mylist:nn { #1 } { #2 }
}
\seq_new:N \l__porst_list_items_seq
\seq_new:N \l__porst_list_output_seq
\cs_new_protected:Npn \porst_mylist:nn #1 #2
{
% clear the output sequence
\seq_clear:N \l__porst_list_output_seq
% split the input at \and
\seq_set_split:Nnn \l__porst_list_items_seq { \and } { #2 }
% append each item to the output sequence
\seq_map_inline:Nn \l__porst_list_items_seq
{
% #1 is the given argument, ##1 represents the current item
\seq_put_right:Nn \l__porst_list_output_seq { #1 { ##1 } }
}
% output the sequence with something between items
\seq_use:Nn \l__porst_list_output_seq {,~} % adjust
}
\ExplSyntaxOff
\begin{document}
\mylist{Element1 \and Element2}
\mylist[\emph]{Element1 \and Element2}
\end{document}
第一步是在\and
标记处拆分输入;尾随和前导空格将被删除;第二步是将“格式化”的项目存储在另一个序列中,以便在项目之间使用一些分隔符来使用该序列。
另一个可能的工具是etoolbox
;但是,如您所见,它要复杂得多。
\documentclass{article}
\usepackage{etoolbox}
\makeatletter
\DeclareListParser*{\andlist@do}{\and}
\newcommand{\andlist@sep}{, }
\newcommand{\mylist}[2][\underline]{%
\def\andlist@output{\@gobble}%
\andlist@do{\andlist@handler{#1}}{#2}%
\andlist@output
}
\appto\nocorrlist{\andlist@sep}
\newcommand{\andlist@handler}[2]{%
\appto\andlist@output{\andlist@sep#1{#2\unskip}}%
}
\makeatother
\begin{document}
\mylist{Element1 \and Element2}
\mylist[\emph]{Element1\and Element2}
\end{document}
答案2
如果您想要一个不需要包的解决方案,请使用括号{}
,但您需要在定义中平衡它们。要在展开时隐藏它们,只需使用\iffalse
.. \fi
:
\def\and{\unskip\iffalse{\fi} \noexpand\underline{\iffalse}\fi}%
\protected@edef\myinternallist{\noexpand\underline{#1}}%
\edef
不能与任意内容一起使用,因此我使用,这意味着您必须用..\protected@def
将整个定义括起来。我还添加了(删除每个块末尾的空格)和一个空格。\makatletter
\makeatother
\unskip
答案3
这里是常见的解决方案,不需要任何expl3
,etoolbox
等,\and
宏可以根据需要定义,我们只需要三行宏:
\def\mylist#1{\mylistA #1\and\and}
\def\mylistA#1\and{\ifx\and#1\and\else \and{#1}\expandafter\mylistA\fi}
\def\and#1{$\underline{\rm #1}$ }
\mylist{Element1 \and Element2}
编辑:Egreg 在此处的评论包含一个问题,即如何仅在项目之间打印分隔符。这很简单。我们只使用三行,无需额外的包,也没有任何麻烦:
\def\mylist#1{\def\mylistS{\def\mylistS{, }}\mylistA #1\and\and}
\def\mylistA#1\and{\ifx\and#1\and\else \and{#1}\expandafter\mylistA\fi}
\def\and#1{\mylistS $\underline{\rm #1}$}
\mylist{Element1 \and Element2}
编辑2:如果有人需要在最后两个项目之间打印另一个分隔符,那么他可以使用以下代码。当然,它稍微复杂一些:
\def\mylist#1{\def\mylistS{}\def\andA##1{\let\andA=\andB}\mylistA #1\and\and}
\def\mylistA#1\and{\ifx\and#1\and \and{}\else \and{#1}\expandafter\mylistA\fi}
\def\and#1{\ifx\mylistS\empty\else\andA{#1}\fi\mylistS \def\mylistS{$\underline{\rm #1}$}}
\def\andB#1{\ifx\and#1\and{ and }\else{, }\fi}
元素之间打印逗号,但最后两个元素之间打印单词“and”。
答案4
如果您不受限制物品的想法的约束,\and
您可能想看看包裹commado
或dowith
我最近在遇到类似问题时发现的包裹。
对于前者,你可以尝试一些类似的事情
\usepackage{commado}
% ...
\newcommand{\myList}[2][\underline]{\doWithCSL{#1}{#2}}
并称之为
\myList{Element1, Element2}
由于这不会在元素之间产生任何空间,因此以下添加将(几乎)重现上述示例
\usepackage{commado}
\usepackage{xspace}
\newcommand{\myul}[1]{\underline{#1}\xspace}
\newcommand{\myem}[1]{\emph{#1}\xspace}
\newcommand{\myList}[2][\myul]{\doWithCSL{#1}{#2}}
% ...
\mylist{Element1, Element2}!
\mylist[\myem]{Element1, Element2}!
(感叹号表示\xspace
如果不需要的话,有助于抑制尾随空格)
所有软件包都是标准分发的一部分。