我是 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
:当前底线划分,\intensite
:xcolor
颜色混合不允许表达,所以必须计算,\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 node
和last number
。)
我已经设置好了大部分样式,以便它们可以根据已知值计算所需的宽度,从而每一列都有最小宽度,并且数字正确对齐。
对于剩余节点的颜色,我计算了从0
到的线性进展base - 1
。
该键letter remaindars
安装了一种样式,使用通常与 LaTeX 计数器一起使用的 LaTeX2e 宏,将 10 ( ) 和 35 ( )every remainder
之间的数字替换为相应的字母。A
F
键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}