Tikz 在 foreach 中定义

Tikz 在 foreach 中定义

我是 foreach 命令的初学者!

\begin{tikzpicture}
\foreach \i in {0,1}{%
        \def\n{23} 
        \node at (0, -\i) {\n} ;
        \def\n{24}
            }
\end{tikzpicture}

是否有可能在 foreach 循环中改变 \def 的值?

谢谢

@Andrew Stacey

在此处输入图片描述

这是我的最终代码:

\documentclass[12pt]{article}
\usepackage{tikz}
\usetikzlibrary{calc}
\begin{document}

\newcommand \DivisionBase[2][2]{ 
%le premier parametre #1 par defaut est donc 2
\begin{tikzpicture}
\def \base{#1}
\def \n{#2}
\def \cd{-0.7} %on peut jouer sur l'inclinaison de l'escalier 
\def \quasiNBdigit {\n} 
% C'est le nombre de digit - 1 ; j'ai mis n par defaut
  \node (a0) at (0,0) {$\n$} ;
  % ai = bi*qi + ri
  \global\let\mya= \n
  \global\let\myb = \base
%  \pgfmathsetmacro \quasiNBdigit {int(log2(\n))}
  \foreach \i in {0,1,...,\quasiNBdigit}{%
  \pgfmathsetmacro \myq {int(\mya/\myb)}
  \pgfmathsetmacro \myr {int(\mya - \myb*\myq)}
  \pgfmathsetmacro \j {\i+1}
  \coordinate (a\i) at (\i, \cd*\i) ;
  \node (b\i) at (\j, \cd*\i) {$\myb$} ;
  \node (q\i) at (\j, \cd*\j) {$\myq$} ;
  \pgfmathsetmacro \intensite {10*(\i+1)}
  \node[fill = red!\intensite] (r\i) at (\i,\cd*\j) {$\myr$} ;
  \coordinate (T) at ($ (a\i)!.5!(b\i) $) ;
  \draw[thick] (T) -- ++(0,-1) ;
  \coordinate (M) at ($ (a\i)!.5!(q\i) $) ;
  \draw[thick] (M) -- ++(1,0) ;
  \ifnum \myq<1
      \breakforeach
  \fi
  \global\let\mya=\myq
  }
\end{tikzpicture}
}


\DivisionBase{57}

\end{document}

答案1

您的主要问题已在评论中得到解答:要么使用关键字\global(或等效的\gdef),要么如 Qrrbrbirlbel 所说,使用循环remember中的键\foreach。以下代码给出了如何使用后者的示例(remember),但我也借此机会尝试以我可能编写的方式重写您的代码 - 您说您是初学者,因此foreach您不应将其视为“答案”,而应将其视为“示例”。

这很大程度上只是个人风格的问题。我稍微展示了如何\def尽可能避免使用 s(或等价物)。我设法将其减少到需要计算的以下值(我还使用了更长、更具描述性的变量名 - 因为这是我的代码版本):

  • \quotient:当前底线划分,
  • \intensitexcolor颜色混合不允许表达,所以必须计算,
  • \quasiNBdigit:我发现最好避免在\foreach列表中使用复杂的表达。

还可以减少定义的坐标数量。同样,这完全是个人选择,并且很大程度上取决于您认为的“可读代码”是什么样子。

\documentclass[12pt]{article}
%\url{https://tex.stackexchange.com/q/654957/86}
\usepackage{tikz}
\usetikzlibrary{calc,arrows.meta}
\begin{document}

\newcommand \DivisionBase[2][2]{ 
  \begin{tikzpicture}[
    yscale=.7, % avoids needing the \cd macro
    >=Latex
  ]
  % I use \pgfmathtruncatemacro throughout to ensure that the result
  % of any calculation is truncated to an integer
  %
  % Work out how many iterations will be needed,
  % the -1 is as the loop starts at 0
  \pgfmathtruncatemacro\quasiNBdigit{ceil(ln(#2)/ln(#1))-1}
  % Place the first node
  \node (a0) at (0,0) {\(#2\)};
  % The only thing that needs to be remembered is the current quotient,
  % We can also do the intensity calculation here
  \foreach[
    remember=\quotient as \quotient (initially #2),
    evaluate=\i as \intensite using 10*(\i+1)
  ] \i in {0,1,...,\quasiNBdigit}{
    % PGFMath uses Mod for truncated division
    \node[fill = red!\intensite] (r\i) at (\i,-\i-1) {\(
      \pgfmathparse{int(Mod(\quotient,#1))}\pgfmathresult
      \)};
    
    % The 'floor' is redundant as we're using pgfmathtruncate macro
    \pgfmathtruncatemacro\quotient {floor(\quotient/(#1))}

    % Calculations can be put in coordinate expressions
    \node (b\i) at (\i+1, -\i) {\(#1\)} ;
    \node (q\i) at (\i+1, -\i-1) {\(\quotient\)} ;
    
    % No need for the remembered coordinates here, just use the
    % calculated coordinates directly
    \draw[thick]
    ($ (\i, -\i)!.5!(b\i) $) -- ++(0,-1)
    ($ (\i, -\i)!.5!(q\i) $) -- ++(1,0)
    ;
  }
  \draw[->] (r\quasiNBdigit.south west) -- (r0.south west);
  \end{tikzpicture}
}

\DivisionBase{57}

\end{document}

这会产生与问题中一样的输出,余数下方有一个箭头(如评论中所要求的)。

答案2

循环之前\global的不是必需的。

可以使用以下方法\global删除循环中的

remember=\myq as \mya (initially #2)

\def\cd{-0.7}

不是很优雅,但由于您在tikzpicture环境内执行此操作,因此您不会覆盖\cd可能存在于该环境之外的宏。

我们可以将很多内容移到标题\pgfmathsetmacros\foreach(但实际上并没有什么不同)。


我建议另一种方法:使用先前放置的节点。

节点确实是由 TikZ 全局定义的,因此您可以引用在范围内或在\foreach该范围之外或下一个循环中的范围体内定义的节点(或坐标)。

我们将所有东西都相对于另一个节点放置,并且有许多键可以对位置进行微调。

对于每个循环,

  • 我们计算
    • 余数(\dbRemainder)和
    • 下一个数字(\dbNextNumber
  • 我们将记住作为下一个\dbNumber

在体内我们将放置

  • \dbNumber最后一个基节点东南角的节点,
  • \dbRemainder位于前一个节点东南角的节点和
  • 底数节点(我们将使用其值键)位于余数节点的东北角(我们也可以再次使用数字的东南角)。

如果到达,0我们将放置一个额外的节点并打破循环。

对于循环的第一个实例,没有@base从上一次运行中命名的节点可用,这就是我放置坐标@base来初始化循环的原因。(与remember密钥类似的是这是我们的初始节点。)


对于每个节点,将使用这些密钥:

  • /division base/every node
  • /division base/every number/
    /division base/every remainder/
    /division base/every base
  • /division base/number <step>//
    /division base/remainder <step>
    /division base/base <step>

(最后一个0节点仅获取every nodelast number。)

我已经设置好了大部分样式,以便它们可以根据已知值计算所需的宽度,从而每一列都有最小宽度,并且数字正确对齐。

对于剩余节点的颜色,我计算了从0到的线性进展base - 1


该键letter remaindars安装了一种样式,使用通常与 LaTeX 计数器一起使用的 LaTeX2e 宏,将 10 ( ) 和 35 ( )every remainder之间的数字替换为相应的字母。AF

connect remainders安装了一种last number样式,将每个剩余部分与前一个部分连接起来。

由于 PGFmath 在内部使用长度寄存器,因此您不能在这里使用任何大数字,除非您回退到低级计数或使用允许更大数字的另一个 TikZ 包或库。

代码

\documentclass[12pt]{article}
\usepackage{tikz}
\usetikzlibrary{arrows.meta,bending}
\makeatletter % always a helpful key
\pgfkeys{/utils/if/.code n args=3{\pgfmathparse{#1}\ifdim\pgfmathresult pt=0pt
  \expandafter\pgfutil@firstoftwo\else\expandafter\pgfutil@secondoftwo\fi
    {\pgfkeysalso{#3}}{\pgfkeysalso{#2}}}}
\makeatother
% like \tikzset but instead for /tikz we use /division base
\newcommand*{\divisionbaseset}{\pgfqkeys{/division base}}
\divisionbaseset{
  letter remainders/.style={
    every remainder/.append style={
      /utils/if={and(\dbRemainder>9,\dbRemainder<36}{
        node contents=\csname @Alph\endcsname{\the\numexpr\dbRemainder-9\relax}}}},
  connect remainders/.style={
    last number/.append style={
      append after command={
        foreach \j[evaluate={\nextj=int(\j-1);}] in {\dbStep,...,2} {
          (@remainder-\j.west) edge[-{Latex[round,bend]},shorten >=1pt,
            out=180,in=-90,#1] (@remainder-\nextj.south)}}}},
  % some defaults
  base/.initial=2,
  every node/.style={align=right},
  every number/.style={
    name=@number, alias=@number-\dbStep,
    at=(@base.south east), anchor=north east, node contents=\dbNumber},
  last number/.style={
    name=@number, alias=@lastnumber,
    at=(@base.south east), anchor=north east, node contents=0,
    text width=width("\dbvo{base}")},
  every base/.style={
    name=@base, alias=@base-\dbStep,
    at=(@remainder.north east), anchor=south west, node contents=\dbvo{base},
    text width={max(width("\dbNextNumber"),width("\dbvo{base}"))},
    append after command={
      edge[thick, to path={(\tikzlastnode.south east)
        -| (\tikzlastnode.west) -- (@remainder.south east)}]()}},
  every remainder/.style={
    name=@remainder, alias=@remainder-\dbStep,
    at=(@number.south east), anchor=north east, node contents=\dbRemainder,
    text width={max(width("\dbNumber"),width("\dbvo{base}"))},
    /utils/exec=\pgfmathsetmacro\dbColor{(\dbRemainder+1)/(\dbvo{base})*100},
    fill=red!\dbColor}}
\newcommand*{\DivisionBase}[2][]{% #1 = keys, #2 = number
\begin{tikzpicture}
\newcommand*\dbvo[1]{\pgfkeysvalueof{/division base/##1}}% local shortcut
\divisionbaseset{#1}           % let's apply our settings
\coordinate (@base);           % let's start somewhere for reference
\foreach \dbStep[
  evaluate={% we calculate the Remainder and also the next number
    \dbRemainder=int(mod(\dbNumber,\dbvo{base}));
    \dbNextNumber=int(\dbNumber/\dbvo{base});
  },        % the next number will be used in the next run as \dbNumber
  remember=\dbNextNumber as \dbNumber as (initially #2),
] in {1,...,#2} {% we could calculate the necessary loops
  \node[         % but I don't mind \breakforeach
    /division base/every node,
    /division base/every number,
    /division base/number \dbStep/.try];
  \node[
    /division base/every node,
    /division base/every remainder,
    /division base/remainder \dbStep/.try];
  \node[
    /division base/every node,
    /division base/every base,
    /division base/base \dbStep/.try];
  \ifnum\dbNextNumber=0
    \node[/division base/every node, /division base/last number];
    \breakforeach
  \fi
}
\end{tikzpicture}}

\begin{document}
\DivisionBase{57}
\DivisionBase[base=135]{10000}
\DivisionBase[letter remainders, base=16]{4089}
\DivisionBase[connect remainders, last number/.append style=blue!50]{57}
\end{document}

输出

在此处输入图片描述 在此处输入图片描述

相关内容