当人们想要建立带有空列的表格或类似地在宏内建立一些活动字符并希望在正确的时间正确的地点扩展它们时,经常会出现这个问题。
显然,由于 TeX 的肠道运动非常精细(因为我们已经有了嘴和胃),事情并没有像预期的那样顺利进行。这个问题以不同的形式被问过很多次,我认为它很有可能成为重复的来源,因此我提出了这个问题。我也有一个 hacky 解决方案,但我认为我们的向导可以让事情变得更有条理。
考虑一下下面这个含有足够多细菌的玩具示例:
TikZ 有一个\matrix
宏,它的作用类似于tabular
环境,即它查找行和列分隔符并形成图形对象。它不必与 TikZ 相关(见下文)
\documentclass[tikz]{standalone}
\usetikzlibrary{matrix}
\begin{document}
\tikz\matrix[execute at empty cell=\node{$\bullet$};]{\themacrotobedefined};
\end{document}
或者你可以考虑
\documentclass{standalone}
\begin{document}
\begin{tabular}{cccc}\themacrotobedefined\end{tabular}
\end{document}
要定义的宏\themacrotobedefined
应该保存字符流
&&&\\&&&\\&&&\\
这样我们就有四列、三行(数字是随机的,你可以随意选择)。这个问题可以像你希望的那样笼统,你可以包括数学字符等。但为了让问题更复杂一点,请使用以下方法
\foreach\x{1,2,3}{
\foreach\y{1,2,3}{
...
}
}
这样宏在每次旋转时都会按顺序构建(再次强调,foreach 是可选的,do/while 也是可以的)。主要目标是如何在不过早扩展它们的情况下将敏感项附加到宏中。纯 TeX/LaTeX/ConteXt 来吧。
我们有相当多的问题与这个问题间接有关,但从未正面涉及,所以我认为它不是重复的(除非我们找到它)。
答案1
这也许是最简单的方法,请注意,单元格宏本身根本没有扩展,但循环变量在构造宏时会扩展。由于 tikz 矩阵代码中存在一些怪异之处,我不得不使用\&
而不是&
,使用 tabular 时,您可以使用&
而不是\noexpand\&
。
\documentclass[tikz]{standalone}
\usetikzlibrary{matrix}
\begin{document}
\def\mycell#1#2{\node{${#1}^2-{#2}^2=\the\numexpr(#1+#2)*(#1-#2)\relax$};}
\def\themacrotobedefined{}
\foreach\x in {1,2,3}{%
\foreach\y in {1,2,3}{%
\xdef\themacrotobedefined{\expandafter\unexpanded\expandafter{\themacrotobedefined}%
\noexpand\mycell{\x}{\y}%
\ifnum\y=3 \noexpand\\\else\noexpand\&\fi}%
}%
}
\texttt{\meaning\themacrotobedefined}
\tikz\matrix[ampersand replacement=\&,execute at empty cell=\node{$\bullet$};]{\themacrotobedefined};
\end{document}
上面的构建是\themacrotobedefined
通过多次递增地重新定义它来构建的,每次迭代都会添加一个单元格。这是必要的,\foreach
因为可扩展内部它使用赋值来迭代循环。
如果您使用可扩展循环映射结构(例如下面的结构),则可以在单个定义中定义宏:
\documentclass[tikz]{standalone}
\usetikzlibrary{matrix}
\def\myloop#1#2#3{%
#3{#1}%
\ifnum#1=#2 \expandafter\gobblesix\fi
\expandafter\myloop\expandafter{\the\numexpr#1+1\relax}{#2}{#3}}
\def\gobblesix#1#2#3#4#5#6{}
\begin{document}
\def\mycell#1#2{\noexpand\node{${#1}^2-{#2}^2=\the\numexpr(#1+#2)*(#1-#2)\relax$};%
\ifnum#2=3 \noexpand\\\else\noexpand\&\fi
}
\def\zmycell#1{\myloop{1}{3}{\mycell{#1}}}
\edef\themacrotobedefined{%
\myloop{1}{3}{\zmycell}%
}
\texttt{\meaning\themacrotobedefined}
\tikz\matrix[ampersand replacement=\&,execute at empty cell=\node{$\bullet$};]{\themacrotobedefined};
\end{document}
答案2
我添加了与 @David 的解决方案类似的解决方案,但使用了宏\gaddto
和\xaddto
。\gaddto
将未展开的参数添加到宏,将\xaddto
展开的参数添加到宏。使用(在循环中)比使用\expandafter\unexpanded\expandafter
等更简单:
\long\def\gaddto#1#2{\expandafter\gdef\expandafter#1\expandafter{#1#2}}
\long\def\xaddto#1#2{\edef\tmp{#2}\expandafter\gaddto\expandafter#1\expandafter{\tmp}}
\def\tobedefined{}
\foreach\x in {1,2,3}{%
\foreach\y in {1,2,3}{%
\gaddto\tobedefined{\mycell}
\xaddto\tobedefined{{\x}{\y}}
\ifnum\y=3 \gaddto\tobedefined{\\}\else\gaddto\tobedefined{\&}\fi
}}
答案3
您的问题似乎是关于通过添加可以扩展或不扩展的材料来准备一些宏。这看起来像是一个一般性问题,但如果专注于此,\foreach
它就会立即变成另一个问题:如何克服\foreach
组中每个项目都这样做的事实?以及如何将\x
、或\y
循环变量转换为它们保存的值?提供的答案中已经解决了这个问题。
有时人们希望不必事先构造这样的宏,而是直接将其插入手头的结构中,例如tabular
或\tikz\matrix
。在有利的情况下,例如tabular
人们知道使用可扩展循环可以做到这一点,但是人们主要要小心 TeX 何时看到&
(\cr
隐藏在 LaTeX 中\\
),以及何时是安全地进行某些赋值的正确时机。使用\numexpr
构造,人们可以在没有任何赋值的情况下做事。即使有人做了赋值,人们也不会不是必须使它们全球化(正如所示\xintFor
)。
但这些都是有利的情况:在不利的情况下,您需要取决于周围环境的行为方式:它会扫描其内容吗?它会将其扩展一倍以上吗?即使在看起来可以接受纯粹通过扩展工作的内容的情况下,也可能会出现意外。例如,的coordinates
似乎TikZ/PGF
只接受将其内容扩展至100
倍,而不是更多。
好的,所以最安全的方法始终是提前构建一个宏,但人们并不清楚这对于tabular
(关于这个主题的问题往往会很快被关闭为重复)来说是不必要的。
然后,既然您已经得到了关于如何使用的答案\foreach
,我提供了一个有点离题的答案,即使用另一个循环,该循环不会执行组中的每个项目。虽然它不是一个可扩展的循环,但如果直接在表格中使用,它就可以工作。然而\tikz\matrix
,我必须回到定义\Macro
之前,正如在大卫的回答看来,对于一个人来说,必须用而不是 来\tikz\matrix
准备宏。\&
&
\documentclass {article}
\usepackage{tikz}
\usetikzlibrary{matrix}
\usepackage{xinttools}
\makeatletter
\let\firstofone\@firstofone
\let\gobble\@gobble
\makeatother
\begin{document}
\begin{tabular}{*5{|c}|}
\hline
\xintFor #1 in {1, 2, 3, 4, 5}\do
{%
\xintFor #2 in {1, 2, 3, 4, 5}\do
{${#1}^2-{#2}^2=\the\numexpr(#1+#2)*(#1-#2)$
\ifnum#2=5 \expandafter\gobble\else\expandafter\firstofone\fi
{&}%
}%
\\\hline
}
%\hline
\end{tabular}
\newcommand*\mycell [2]
{\node{${#1}^2-{#2}^2=\the\numexpr(#1+#2)*(#1-#2)\relax$};}
\def\Macro{}
\xintFor #1 in {1, 2, 3, 4, 5}\do
{%
\xintFor #2 in {1, 2, 3, 4, 5}\do
{%
\odef\Macro {\Macro \mycell {#1}{#2}}%
\ifnum #2<5
\odef\Macro {\Macro \&}% use of \& in place of & for TikZ
\fi
}%
\odef\Macro {\Macro \\}%
}%
\ttfamily
\meaning\Macro
\begin{tikzpicture}
\matrix[ampersand replacement = \&, execute at empty cell=\node{$\bullet$};]
{\Macro };
\end{tikzpicture}
\end{document}
在上面的代码中\odef\Macro{<stuff>}
定义\Macro
为一次扩展的<stuff>
。