带有“foreach 变量”的 calc 库的难以理解的行为

带有“foreach 变量”的 calc 库的难以理解的行为

如果我编译以下代码:

\documentclass[12pt]{article}

\usepackage[french]{babel}
\usepackage{tikz}
\usetikzlibrary{calc}


\begin{document}

\begin{tikzpicture}
\foreach \i in {(0,0),(0,2.1),(2.1,2.1),(2.1,0)} {
\fill[lightgray] \i rectangle ($(\i+(0.7,0.7)$); }
\draw (0,0) grid[step=0.7] (2.8,2.8);
\end{tikzpicture}

\end{document}

我没有遇到任何问题,这给了我想要的结果。但是“$”之间的表达式括号不正确。

如果我替换$(\i+(0.7,0.7)$为,$\i+(0.7,0.7)$则会收到以下错误消息:

Runaway argument?
\i +(0.7,0.7)$); \pgffor@endhook \ifx \pgffor@assign@after@code \pgfutil@empty 
\ETC.
! Paragraph ended before \tikz@cc@parse@factor was complete.
<to be read again> 
               \par 
l.15 

如果我替换$(\i+(0.7,0.7)$$(\i+(0.7,0.7))$或,$(\i)+(0.7,0.7)$我会收到以下错误消息:

! Package tikz Error: + or - expected.

See the tikz package documentation for explanation.
Type  H <return>  for immediate help.
 ...                                              

l.12 ...htgray] \i rectangle ($(\i)+(0.7,0.7)$); }

这是一个错误还是我做错了什么?(或者这只是一个正确的奇怪语法?)

答案1

在原帖作者给出的例子中,

\fill[lightgray] \i rectangle ($(\i+(0.7,0.7)$); 

可以(并且可能应该)

\fill[lightgray] \i rectangle ++(0.7,0.7); 

但是,假设这是一个 MWE,并且实际应用不可避免地需要使用该calc库,那么这是一个扩展问题(正如已经指出的那样)。当calc使用该库时,以下方法可能适用:

\documentclass[tikz,border=5]{standalone}
\usetikzlibrary{calc}
\begin{document}
\begin{tikzpicture}
\foreach \i in {(0,0),(0,2.1),(2.1,2.1),(2.1,0)} {
\fill let \p1=\i in [lightgray] (\p1) rectangle ($(\p1)+(0.7,0.7)$); }
\draw (0,0) grid[step=0.7] (2.8,2.8);
\end{tikzpicture}
\end{document}

虽然

\fill let \p1=\i in [lightgray] (\p1) rectangle (\x1+.7, \y1+.7);

(我认为) 会稍微更有效率。

在此处输入图片描述

但如果你想生活在边缘,就把

\makeatletter
\def\pgffor@scanround(#1)#2,{\def\pgffor@value{#1#2}\pgffor@scanned}

在你的序言中。然后($(\i)+(0.7,0.7)$)可以在 foreach 循环中使用。

答案2

我认为这是这个的复制品在 TikZ 中使用数学长话短说,这是一种奇怪的交互,它需要一个明确的(因此完全展开的)左括号才能正确获取上下文,并使用右括号来\i正确完成语法。如果它看到另一个出错了。正确的方法是将所有内容放在它们自己的上下文中,并且非常冗长{}

这里有一个更好的方法来了解发生了什么,它很好地演示了 TeX 和 TikZ 如何就解析标记进行通信(尽管非常不直观)。请注意某些地方缺少括号,包括列表。

出现这种情况的主要原因是 TikZ 通过调查流中发现的下一个字符来分支。因此,如果您分支到错误的位置并在之后输入正确的术语,它就无法返回并正确分支。我认为在这种类型的编程中您无法消除所有这些问题。

\documentclass[tikz]{standalone}
\usetikzlibrary{calc}
\begin{document}
\begin{tikzpicture}
\foreach \i in {{(0,0},{(0,2.1},{(2.1,2.1},{(2.1,0}} {
\fill[lightgray] \i) rectangle ($(\i)+(0.7,0.7)$); }
\draw (0,0) grid[step=0.7] (2.8,2.8);
\end{tikzpicture}
\end{document}

在此处输入图片描述

答案3

值得注意的是,下面的代码没有多余的括号,并且设置了正确匹配的括号,可以编译并给出预期的结果:

\documentclass[12pt]{article}

\usepackage[french]{babel}
\usepackage{tikz}
\usetikzlibrary{calc}

\usepackage{xinttools}

\begin{document}

% \begin{tikzpicture}
% \foreach \i in {(0,0),(0,2.1),(2.1,2.1),(2.1,0)} {
% \fill[lightgray] \i rectangle ($(\i+(0.7,0.7)$); }
% \draw (0,0) grid[step=0.7] (2.8,2.8);
% \end{tikzpicture}

\begin{tikzpicture}
\xintForpair  #1#2 in {(0,0),(0,2.1),(2.1,2.1),(2.1,0)} \do {
\fill[lightgray] (#1,#2) rectangle ($(#1,#2)+(0.7,0.7)$); }
\draw (0,0) grid[step=0.7] (2.8,2.8);
\end{tikzpicture}

\end{document}

答案4

所有先前的解决方案都很完美。其中一些解释了为什么 calc 会在您的代码中出错。其中一些解释了您应该进行哪些更改才能使用foreach贯穿“点”的函数(x_i,y_i)

在我看来,你的foreachTikZ 精神是“错误的”:

  • 当您说\coordinate (A) at (x,y);然后A代表x,y而不是(x,y)并且您将其用作时(A)
  • 当您说let \p1=(x,y)然后\p1代表x,y而不是(x,y)并且您将其用作时(\p1)

所以我认为当我们想要实现foreach这一点时,我们应该

\foreach \i in {{x_1,y_1},...,{x_n,y_n}}

然后将其用作(\i)

在你的情况下,这将变成:

\documentclass[tikz,varwidth,border=5]{standalone}
\usetikzlibrary{calc}
\begin{document}
  \begin{tikzpicture}
    \foreach \i in {{0,0},{0,2.1},{2.1,2.1},{2.1,0}}
      \fill[lightgray] (\i) rectangle ($(\i)+(0.7,0.7)$); 
    \draw (0,0) grid[step=0.7] (2.8,2.8);
  \end{tikzpicture}
\end{document} 

在此处输入图片描述

相关内容