我正在尝试在循环中定义多个复杂的newcommand
s foreach
。这听起来很简单,但为什么这么难?
天真的尝试:
\documentclass{report}
\usepackage{tikz}
\foreach \i in {first,second,third}{
\expandafter\newcommand\csname\i\endcsname[3]{ % IYO: how readable is this "naive try"?
command name: \i
\begin{minipage}[\textwidth]
first argument: ##1
second argument: ##2
third argument: ##3
\end{minipage}
}
}
\begin{document}
\first{1}{2}{3}\\
\second{a}{b}{c}\\
\third{Is}{\LaTeX}{cool?}\\
\end{document}
这吐了:
! Undefined control sequence.
l.22 \first
{1}{2}{3}\\
好的,\newcommand
没有在 的范围之外定义命令\foreach
。添加\global
任何地方有帮助吗?如果有,它无法弄清楚将其放在哪里。(基本的 LaTeX 经验法则:没有什么是直观的)
我必须承认这是头痛。幸运的是,那里有互联网,所以感谢你们和 Stack 网站。途中的主要陷阱:
\global
似乎只适用于\def
所以使用\gdef
而不是\newcommand
\i
不会扩展为<the loop counter>
但为\iota
,因此使用\edef
因此使用\xdef
- 该
\def
系列处理参数规范的方式不同,因此使用##1##2##3
而不是[3]
(这看起来更美观,可读性更强,易于维护。我爱##1##2##3
。有一天我需要 5 个参数而不是 3 个,我所要做的就是.. 别介意。) - ..我甚至不敢要求支持可选参数。毕竟,这才 2016 年。
最后,这确实可以编译:
\documentclass{report}
\usepackage{tikz}
\foreach \i in {first,second,third}{
\expandafter\xdef\csname\i\endcsname##1##2##3{ % yum
command name: \i
% \begin{minipage}[\textwidth]
first argument: ##1
second argument: ##2
third argument: ##3
% \end{minipage}
}
}
\begin{document}
\first{1}{2}{3}\\
\second{a}{b}{c}\\
\third{What}{a}{pain!}\\
\end{document}
..这让我明白:
但是,如果我取消注释与环境有关的行,它就不再起作用minipage
:
! Use of \@iminipage doesn't match its definition.
\IfFileExists #1#2#3->\openin \@inputcheck #1
\ifeof \@inputcheck \ifx \inpu...
l.18 }
(顺便说一句,这真是一个令人印象深刻的线索。谢谢你,编译器。也许我应该让我的思想更加坚定,否则你和我将和谐地融合在一起,再也不会是两个。)
简而言之,考虑以下情况:
- 为什么
minipage
不工作?我该如何让它工作? - 有没有什么办法可以让我拥有并
\first
支持可选参数?\second
\third
- 我是不是在重新发明轮子?调查方法不对?有没有更好的方法?
就是这样。感谢您的耐心。
非常感谢您的到来。 :)
答案1
在我看来,你使用了错误的工具:
\documentclass{report}
\usepackage{xparse}
\ExplSyntaxOn
\NewDocumentCommand{\xforeach}{s m +m}
{
\IfBooleanTF{#1}
{
\clist_map_inline:on { #2 } { #3 }
}
{
\clist_map_inline:nn { #2 } { #3 }
}
}
\cs_generate_variant:Nn \clist_map_inline:nn { o }
\ExplSyntaxOff
\xforeach{first,second,third}
{
\expandafter\newcommand\csname#1\endcsname[3]{%is this "naive try"?
\par\bigskip
command name: #1
\par\nopagebreak\medskip
\begin{minipage}{.5\textwidth}
first argument: ##1
second argument: ##2
third argument: ##3
\end{minipage}
}
}
\setlength{\parindent}{0pt} % just for the example
\begin{document}
\first{1}{2}{3}
\second{a}{b}{c}
\third{Is}{\LaTeX}{cool?}
\end{document}
如您所见,当前正在处理的项目用#1
而不是表示,\i
并且##1
可以像往常一样用作内部参数。
如果需要使用宏扩展到列表,请使用\xforeach*
:
\newcommand{\mylist}{first,second,third}
\xforeach*{\mylist}{<whatever>}
答案2
您需要防止\begin
扩展\xdef
,并且小页面宽度不需要,{}
除了[]
您几乎已经到达那里之外,我还修复了一些白色空间问题。
\documentclass{report}
\usepackage{tikz}
\foreach \i in {first,second,third}{%
\expandafter\xdef\csname\i\endcsname##1##2##3{% yum
command name: \i
\noindent\noexpand\begin{minipage}{\textwidth}
first argument: ##1
second argument: ##2
third argument: ##3
\noexpand\end{minipage}%
}%
}
\begin{document}
\first{1}{2}{3}
\second{a}{b}{c}
\third{What}{a}{pain!}
\end{document}
也许我会使用不同的循环宏,而不会引入不需要的组:
\documentclass{report}
% \usepackage{tikz}
\newcommand\dodefs[1]{%
\expandafter\newcommand\csname#1\endcsname[3][?]{% yum
command name: #1
\noindent\begin{minipage}{\textwidth}
first argument: ##1
second argument: ##2
third argument: ##3
\end{minipage}%
}%
}
\makeatletter
\@for\i:=first,second,third\do{%
\expandafter\dodefs\expandafter{\i}
}
\makeatother
\begin{document}
%\show\first
\first[1]{2}{3}
\first{2}{3}
\second[a]{b}{c}
\third[What]{a}{pain!}
\end{document}