我尝试编写宏,比如说,它自动为一些生成的宏\foo
提供计数器和类似的命令,使用包方法对生成的命令进行微调。 \listofsomething
xkeyval
我使用该tocloft
包进行计数器和生成(尽管我认为我可以自己\listofsomething
定义一些类似的方法)\@starttoc
但是,我发现了扩展问题,并且下面的\expandafter
for组合不起作用\newlistof
(我省略了\expandafter
MWE 中的语句)。
代码没有抱怨,但是输出是错误的,因为使用generator
宏\foo
进行另一个宏定义将提供“正确”的\listofsomething
命令,但是\listofname
从最后定义的命令开始使用,并且所有条目都进入同一个列表,这是不想要的。
(当然,真正的代码有很多功能(或将有很多)),但我将其归结为最低限度。例如,如果没有明确给出键值,则包含的宏\listofname
将从第二个参数自动生成,但我在这里省略了这一点)\foo
- 我并不怀疑
xkeyval
这应该归咎于此(因为使用它之外的关键宏并将它们重新定义为其他内容只会提供最新的内容,而不是以前的内容。 \begingroup...\endgroup
有助于保持命名空间在 中的整洁\foo
,但\listofsomething
定义的宏在\newlistof
之外无法使用\foo
- 此处定义的宏的输出格式
\foo
并不重要。目前它只是一个用于测试的小“标题”。 - 该
\foo
命令是为了\@onlypreamble
使用而设计的,但为了简单起见,我在这里省略了此功能。
这个问题的可能解决方案是什么?或者之后有没有办法使用\global
宏\listofsomething
,以便在内部使用分组\foo
(也许一些\let
等等?)
\documentclass{article}
\usepackage{tocloft}
\usepackage{xkeyval}
\makeatletter
\define@key{foo}{listofname}{%
\def\foolistofname{#1}%
}
\define@key{foo}{countername}{%
\def\foocountername{#1}%
}
\define@key{foo}{listext}{%
\def\foolistext{#1}%
}
\makeatother
\newcommand{\foo}[2][]{%
\setkeys{foo}{#1}
\newlistof{\foocountername}{\foolistext}{\foolistofname}
\expandafter\newcommand\csname #2\endcsname[1]{%
\refstepcounter{\foocountername}%
\addcontentsline{\foolistext}{section}{\csname the\foocountername\endcsname~ ##1}
{\vspace*{50pt}\noindent\bfseries \large \csname the\foocountername\endcsname~ ##1\normalsize\normalfont}
}
}
\newcommand{\listofmarxbrothersname}{List of Marx Brothers}
\newcommand{\listofstoogesname}{List of the Stooges}
% Define the command \marxbrothers and its \listof...
\foo[listofname={\listofmarxbrothersname},listext=mb,countername={mb}]{marxbrothers}
% Define the command \stooges and its \listof...
\foo[listofname=\expandafter{\listofstoogesname},listext=st,countername={stooges}]{stooges}
\def\foocountername{monty}
\def\foolistext{monty}
\def\foolistofname{List of Monty Pythons}
\newlistof{\foocountername}{\foolistext}{\foolistofname}
\begin{document}
\listofstooges
\listofmb%
\marxbrothers{Groucho}
\marxbrothers{Zeppo}
\marxbrothers{Harpo}
\marxbrothers{Chico}
\marxbrothers{Gummo}
\stooges{Curly}
\stooges{Moe}
\stooges{Larry}
\end{document}
快照显示所有条目都进入了同一份列表,使用了错误的计数器和错误的List of ....
标题。应该有两个列表。
答案1
你用什么
\foo[listofname={\listofmarxbrothersname},listext=mb,countername={mb}]{marxbrothers}
本质上
\newlistof{\foocountername}{\foolistext}{\foolistofname}
\newcommand\marxbrothers[1]{%
\addcontentsline{\foolistext}{section}{\csname the\foocountername\endcsname~##1}%
...%
}
这当然不是你想要的,因为\marxbrothers
将使用当前的的值\foocountername
等等。
你需要一个巨大的\begingroup\edef\x{\endgroup...}\x
技巧,写起来很麻烦,\expandafter
而且\noexpand
涉及很多关键位置。我不想这么做。
这是一个更加简单的实现expl3
。
\documentclass{article}
\usepackage{tocloft}
\usepackage{xparse}
\ExplSyntaxOn
\keys_define:nn { christian/foo }
{
listofname .tl_set:N = \l_foo_listofname_tl,
countername .tl_set:N = \l_foo_countername_tl,
listext .tl_set:N = \l_foo_listext_tl,
}
\NewDocumentCommand{\foo}{O{}m}
{
\keys_set:nn { christian/foo } { #1 }
\__foo_create_commands:nVVV {#2} \l_foo_countername_tl \l_foo_listext_tl \l_foo_listofname_tl
}
\cs_new_protected:Nn \__foo_create_commands:nnnn
{
\newlistof{#2}{#3}{#4}
\cs_new_protected:cpn { #1 } ##1
{
\refstepcounter{#2}
\addcontentsline{#3}{section}{\use:c{the#2}~##1}
\group_begin:
\par
\vspace*{20pt}
\noindent\large\bfseries\use:c{the#2}~##1\par
\group_end:
}
}
\cs_generate_variant:Nn \__foo_create_commands:nnnn { nVVV }
\ExplSyntaxOff
\newcommand{\listofmarxbrothersname}{List of Marx Brothers}
\newcommand{\listofstoogesname}{List of the Stooges}
% Define the command \marxbrothers and its \listof...
\foo[listofname={\listofmarxbrothersname},listext=mb,countername={mb}]{marxbrothers}
% Define the command \stooges and its \listof...
\foo[listofname=\listofstoogesname,listext=st,countername={stooges}]{stooges}
\begin{document}
\listofstooges
\listofmb
\marxbrothers{Groucho}
\marxbrothers{Zeppo}
\marxbrothers{Harpo}
\marxbrothers{Chico}
\marxbrothers{Gummo}
\stooges{Curly}
\stooges{Moe}
\stooges{Larry}
\end{document}
在我看来,\foo
应该有两个强制参数,并且可能应该将键标记为必需,因为当您使用新命令定义新列表时,需要所有这三个。