我不明白下面的代码有什么问题:
\documentclass[a4paper]{article}
\usepackage[T1]{fontenc}
\usepackage{calc}
\usepackage{ifthen}
\usepackage[french]{babel}
\newcounter{a}
\newcounter{b}
\newcommand{\reste}[2]{
\setcounter{a}{#1}
\setcounter{b}{#2}
\whiledo{\(\thea > \theb\) \OR \(\thea = \theb\)}{
\setcounter{a}{\thea - \theb}
}
\thea
}
\newcounter{p}
\newcounter{q}
\newcounter{t}
\newcommand{\pgcd}[2]{
\setcounter{p}{#1}
\setcounter{q}{#2}
\whiledo{\theq > 0}{
\setcounter{t}{\reste{\thep}{\theq}}
\setcounter{p}{\theq}
\setcounter{q}{\thet}
}
\thep
}
\begin{document}
Le reste dans la division entière de $29$ par $6$ est $\reste{29}{6}$.
Le reste dans la division entière de $30$ par $6$ est $\reste{30}{6}$.
Le plus grand commun diviseur de $29$ et $6$ est $\pgcd{29}{6}$.
\end{document}
答案1
您需要使用纯扩展:
\documentclass[a4paper]{article}
\usepackage[T1]{fontenc}
\usepackage[french]{babel}
\makeatletter
\newcommand{\reste}[2]{%
\ifnum#1<#2
\expandafter\@firstoftwo
\else
\expandafter\@secondoftwo
\fi
{#1}{\expandafter\reste\expandafter{\the\numexpr#1-#2}{#2}}%
}
\newcommand{\pgcd}[2]{%
\ifnum#1<#2
\expandafter\@firstoftwo
\else
\expandafter\@secondoftwo
\fi
{\@pgcd{#2}{#1}}{\@pgcd{#1}{#2}}%
}
\newcommand{\@pgcd}[2]{%
\ifnum#2=0
\expandafter\@firstoftwo
\else
\expandafter\@secondoftwo
\fi
{#1}{\expandafter\@@pgcd\expandafter{\expanded{\reste{#1}{#2}}}{#2}}%
}
\newcommand{\@@pgcd}[2]{\@pgcd{#2}{#1}}
\makeatother
\begin{document}
Le reste dans la division entière de $29$ par $6$ est $\reste{29}{6}$.
Le reste dans la division entière de $30$ par $6$ est $\reste{30}{6}$.
Le plus grand commun diviseur de $29$ et $6$ est $\pgcd{29}{6}$.
Le plus grand commun diviseur de $42$ et $12$ est $\pgcd{42}{12}$.
\end{document}
该\reste
宏被递归调用,直到第一个参数小于第二个参数。
类似地,\pgcd
检查其参数的大小,必要时反转它们。然后它调用\@pgcd
递归调用自身计算其余部分,就像往常一样。辅助函数\@@pgcd
对于反转参数很有用,并且更容易用 来探究第一个参数\expandafter
。
使用\expanded
允许使用\reste
而不必担心提供结果所需的扩展次数。
这是使用更高级工具的不同版本:
\documentclass[a4paper]{article}
\usepackage[T1]{fontenc}
\usepackage[french]{babel}
\usepackage{xparse}
\ExplSyntaxOn
\cs_set_eq:NN \reste \int_mod:nn
\NewExpandableDocumentCommand{\pgcd}{mm}
{
\int_compare:nTF { #1 < #2 }
{ \chauvin_pgcd:nn { #2 } { #1 } }
{ \chauvin_pgcd:nn { #1 } { #2 } }
}
\cs_new:Nn \chauvin_pgcd:nn
{
\int_compare:nTF { #2 = 0 }
{ #1 }
{
\chauvin_pgcd:nn { #2 } { \int_mod:nn { #1 } { #2 } }
}
}
\ExplSyntaxOff
\begin{document}
Le reste dans la division entière de $29$ par $6$ est $\reste{29}{6}$.
Le reste dans la division entière de $30$ par $6$ est $\reste{30}{6}$.
Le plus grand commun diviseur de $29$ et $6$ est $\pgcd{29}{6}$.
Le plus grand commun diviseur de $42$ et $12$ est $\pgcd{42}{12}$.
\end{document}
答案2
关于 (La)TeX 如何工作的一些初步说明:
该命令不仅可以传递表示计数器值的 0..9 范围内的数字标记,还可以传递(不可扩展的)标记以漂亮地打印干扰计算和赋值的值。\the⟨counter⟩
\setcounter
而是使用。\number\value{⟨counter⟩}
除了 expl3 的误导性术语(其中大量使用“功能”一词)之外,LaTeX 的编程范式不是像 C++ 或 Java 等高级编程语言那样的过程式/功能式,而是基于宏的声明式和符号式,其中符号由所谓的标记形成,并且在扩展阶段符号/标记被其他符号/标记替换。
在 Knuth 的消化过程类比中,TeX 具有
- 眼睛
- 消化道
- 能够产生代币并将其放入嘴里,从而开始消化过程。
TeX 的眼睛读取 .tex 输入文件。TeX 由此将输入作为一组指令,用于生成标记并将这些标记逐个放入其口中。因此,这些标记形成一个“标记流”,其元素逐个通过 TeX 的消化道。
一个(可扩展的)标记的扩展 - 即用其他标记替换该标记(以及可能构成其参数的那些标记) - 发生在标记通过 TeX 的食道传输时。
这个类比中的分配(定义宏、为\count
寄存器分配值等等)发生在 TeX 的胃里。
TeX 消化过程的最终结果将是生成的输出文件(.pdf 文件 / .dvi 文件、.log 文件、辅助文件(例如 .aux 文件和 .toc 文件以及 .lot / .lof 文件等))以及写入控制台的内容。
这里的关键一点是:
TeX-⟨数字⟩在扩展阶段,必须在 TeX 的喉咙中形成数量,这样形成 TeX 的标记序列才能形成⟨数字⟩-quantity 在 TeX 的“胃”中可用,在那里执行分配等操作。
\setcounter
意味着在 LaTeX 的胃中将\count
一个值分配给所讨论的 LaTeX 计数器下的寄存器,该值通过 LaTeX 的喉管(发生扩展的地方)作为标记序列传递,形成 TeX-⟨数字⟩-数量。
因此,扩展宏的结果不仅仅是最终值。结果是一组插入到标记流中的标记/符号。
例如,
\newcommand{\reste}[2]{%
\setcounter{a}{#1}%
\setcounter{b}{#2}%
\whiledo{\(\thea>\theb\)\OR\(\thea=\theb\)}{%
\setcounter{a}{\thea-\theb}%
}%
\thea
}
扩展的结果\reste{29}{6}
不仅仅是来自 的最终结果\thea
。
结果将是删除 token 序列
\restecontrol-word-token
,,,,,,,,
{explicit catcode-1(begin-group)-character-token
2explicit catcode-12(other)-character-token
9explicit catcode-12(other)-character-token
}explicit catcode-2(end-group)-character-token
{explicit catcode-1(begin-group)-character-token
6explicit catcode-12(other)-character-token
}explicit catcode-2(end-group)-character-token
从标记流中取出标记序列,而不是插入标记序列
\setcountercontrol-word-token
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
{explicit catcode-1(begin-group)-character-token
aexplicit catcode-11(letter)-character-token
}explicit catcode-2(end-group)-character-token
{explicit catcode-1(begin-group)-character-token
2explicit catcode-12(other)-character-token
9explicit catcode-12(other)-character-token
}explicit catcode-2(end-group)-character-token
\setcountercontrol-word-token
{explicit catcode-1(begin-group)-character-token
bexplicit catcode-11(letter)-character-token
}explicit catcode-2(end-group)-character-token
{explicit catcode-1(begin-group)-character-token
6explicit catcode-12(other)-character-token
}explicit catcode-2(end-group)-character-token
\whiledocontrol-word-token
{explicit catcode-1(begin-group)-character-token
\(control-symbol-token
\theacontrol-word-token
>explicit catcode-12(other)-character-token
\thebcontrol-word-token
\)control-symbol-token
\ORcontrol-word-token
\(control-symbol-token
\theacontrol-word-token
=explicit catcode-12(other)-character-token
\thebcontrol-word-token
\)control-symbol-token
}explicit catcode-2(end-group)-character-token
{explicit catcode-1(begin-group)-character-token
\setcountercontrol-word-token
{explicit catcode-1(begin-group)-character-token
aexplicit catcode-11(letter)-character-token
}explicit catcode-2(end-group)-character-token
{explicit catcode-1(begin-group)-character-token
\theacontrol-word-token
-explicit catcode-12(other)-character-token
\thebcontrol-word-token
}explicit catcode-2(end-group)-character-token
}explicit catcode-2(end-group)-character-token
\theacontrol-word-token
进入标记流。
该移除和插入在 LaTeX 的食道中进行。
有了这些知识,就会发现
\setcounter{t}{\reste{\thep}{\theq}}
无法工作:
指令的第二个参数\setcounter
应为一组标记,其元素在扩展后仅产生 TeX 的组成部分 -⟨数字⟩-数量。
但是扩展的结果\reste{\thep}{\theq}
(位于\setcounter{t}{...}
-directive 的第二个参数内)不仅会产生 TeX 的组成部分,⟨数字⟩-数量。
该结果还包含与表示 TeX 组件无关的标记 -⟨数字⟩-数量。
例如,(嵌套)指令\setcounter{a}{\thep}
和\setcounter{b}{\theq}
。
例如,\whiledo
-指令。
(嵌套)\setcounter
指令(位于其他指令之下)产生的标记不表示 TeX 的数字/组件 -⟨数字⟩-数量,但表示将值分配给 LaTeX 计数器/将值分配给\count
所讨论的 LaTeX 计数器下的 TeX 寄存器的操作。当该指令的底层寄存器赋值在 LaTeX 的胃中执行时
,来自嵌套指令的这些赋值表示标记\setcounter
会扰乱周围的指令。\setcounter{t}{...}
\count
使用\whiledo
-directive 时,结果不是通过仅进行扩展(在 LaTeX 的喉咙中)来提供的。\whiledo
还(在 LaTeX 的胃中)执行临时分配。这些临时分配所依据的标记不是产生 TeX 组件的标记-⟨数字⟩-数量。因此它们也“扰乱”了-\setcounter{t}{...}
分配。
(在“嵌套”\setcounter
指令\setcounter{a}{\thep}
及其\setcounter{b}{\theq}
本身中可能存在基本相同的问题:
与它不同,它不能保证提供一组标记,这些标记的元素只产生 TeX-\number\value{⟨counter⟩}
\the⟨counter⟩
⟨数字⟩-数量。可能存在一些用于“漂亮打印”的东西/标记,它们不能是 TeX 的组成部分-\the⟨counter⟩
⟨数字⟩-数量,因此“扰乱”了-\setcounter
分配。)
如何解决这个问题?
例如\reste
,你可以有一个可选参数,该参数表示要插入到这些标记后面的标记,这些标记会产生将所需的值分配给 LaTeX-counter 的标记a
。
尽可能少地修改代码,您可能可以得到如下期望的结果:
\documentclass[a4paper]{article}
\usepackage[T1]{fontenc}
\usepackage{calc}
\usepackage{ifthen}
\usepackage[french]{babel}
\newcounter{a}
\newcounter{b}
\newcommand{\reste}[3][\thea]{%
\setcounter{a}{#2}%
\setcounter{b}{#3}%
\whiledo{\(\number\value{a}>\number\value{b}\)\OR\(\number\value{a}=\number\value{b}\)}%
{\setcounter{a}{\number\value{a}-\number\value{b}}}%
#1%
}
\newcounter{p}
\newcounter{q}
\newcounter{t}
\newcommand{\pgcd}[3][\thep]{%
\setcounter{p}{#2}%
\setcounter{q}{#3}%
\whiledo{\number\value{q}>0}{%
\reste[{\setcounter{t}{\number\value{a}}}]{\number\value{p}}{\number\value{q}}%
\setcounter{p}{\number\value{q}}%
\setcounter{q}{\number\value{t}}%
}%
#1%
}
\begin{document}
Le reste dans la division entière de $29$ par $6$ est $\reste{29}{6}$.
Le reste dans la division entière de $30$ par $6$ est $\reste{30}{6}$.
Le plus grand commun diviseur de $29$ et $6$ est $\pgcd{29}{6}$.
\end{document}
如果\numexpr
有 ε-TeX 扩展,则根本不需要使用 LaTeX 计数器就可以获得结果。
不要使用 LaTeX 计数器,而是使用宏参数在扩展级联期间保存值:
\documentclass[a4paper]{article}
\usepackage[T1]{fontenc}
\usepackage[french]{babel}
\usepackage{amsmath}
\makeatletter
\newcommand\PassFirstToSecond[2]{#2{#1}}%
\newcommand{\reste}[3]{%
% #1 = marker what the remainder shall look like;
% Assume: (integer divisor) = (integer dividend)*(integer number K) + (integer remainder)
% p/P = remainder positive
% s/S = remainder with same sign as dividend
% v/V or anything else = remainder with smallest absolute value
% #2 = divisor
% #3 = dividend
\romannumeral0%
\ifnum#3=0 %
\expandafter\@firstoftwo
\else
\expandafter\@secondoftwo
\fi
{ \text{non-défini}}%
{%
\PassFirstToSecond{#1}{%
\ifnum#3<0 \expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi
{\PassFirstToSecond{-}}%
{\PassFirstToSecond{}}%
{%
\ifnum#2<0 \expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi
{\PassFirstToSecond{-}}%
{\PassFirstToSecond{}}%
{%
\expandafter\PassFirstToSecond\expandafter{\number\ifnum#3<0 -\fi\number#3}{%
\expandafter\PassFirstToSecond\expandafter{\number\ifnum#2<0 -\fi\number#2}{\resteloop}%
}%
}%
}%
}%
}%
}%
\@ifdefinable\gobbletoexclam{\long\def\gobbletoexclam#1!{}}%
\@ifdefinable\forkpsv{\long\def\forkpsv#1!p!s!v!P!S!V!#2#3!!!!{#2}}%
\newcommand\psvfork[4]{%
% #1 = argument to check
% #2 = tokens in case argument to check is p or P
% #3 = tokens in case argument to check is s or S
% #4 = tokens in case argument to check is something else.
\if\relax\detokenize\expandafter{\gobbletoexclam#1!}\relax
\expandafter\@firstoftwo
\else
\expandafter\@secondoftwo
\fi
{%
\forkpsv!#1!s!v!P!S!V!{#2}% <- case p
!p!#1!v!P!S!V!{#3}% <- case s
!p!s!#1!P!S!V!{#4}% <- case v
!p!s!v!#1!S!V!{#2}% <- case P
!p!s!v!P!#1!V!{#3}% <- case S
!p!s!v!P!S!#1!{#4}% <- case V
!p!s!v!P!S!V!{#4}% <- case something else without !
!!!!%
}%
{#4}% <- case something else with !
% The fork internally has eight branches:
% 1. p
% 2. s
% 3. v
% 4. P
% 5. S
% 6. V
% 7. something else without "!"
% 8. something else with "!"
% Branches 1 and 4 collapse into one branch.
% Branches 2 and 5 collapse into one branch.
% Branches 3, 6, 7 and 8 collapse into one branch.
% Thus you could instead implement a fork with six branches:
% 1. p
% 2. s
% 3. P
% 4. S
% 5. something else without "!"
% 6. something else with "!"
% and collapse branches 1 and 3 into one branch
% and branches 2 and 4 into one branch
% and branches 5 and 6 into one branch.
% But this way introducing different behavior for case v or V
% than for case something else with or without "!" is more
% easy if needed. (You may wish the triggering of error-
% messages in the latter case rather than the same behavior as in
% case v/V.)
}%
\newcommand{\resteloop}[5]{%
% #1 = absolute value of divisor (digit sequence)
% #2 = absolute value of dividend (digit sequence)
% #3 = sign of divisor (empty = not negative; - = negative)
% #4 = sign of dividend (empty = not negative; - = negative)
% #5 = marker what the remainder shall look like;
% Assume: (integer divisor) = (integer dividend)*(integer number K) + (integer remainder)
% p/P = remainder positive
% s/S = remainder with same sign as dividend
% v/V or anything else = remainder with smallest absolute value
\ifnum#1<#2 \expandafter\@secondoftwo\else\expandafter\@firstoftwo\fi
{\expandafter\resteloop\expandafter{\number\numexpr#1-#2\relax}{#2}{#3}{#4}{#5}}%
{%
\psvfork{#5}{%
%Case p/P:
\ifx\relax#3\relax\expandafter\@secondoftwo\else\expandafter\@firstoftwo\fi
{%
\@firstofone{\expandafter} \number\numexpr\number#3#1+#2\relax
}%
{%
\@firstofone{\expandafter} \number#3#1%
}%
}{%
%Case s/S:
\ifx\relax#3\relax\expandafter\@secondoftwo\else\expandafter\@firstoftwo\fi
{%
\ifx\relax#4\relax\expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi
{%remainder negative module positive
\@firstofone{\expandafter} \number\numexpr\ifnum#1=0 \else#2+\fi\number#3#1\relax
}{% remainder negative module negative
\@firstofone{\expandafter} \number#3#1%
}%
}{%
\ifx\relax#4\relax\expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi
{% remainder positive module positive
\@firstofone{\expandafter} \number#3#1%
}{% remainder positive module negative
\@firstofone{\expandafter} \number\numexpr\ifnum#1=0 \else-#2+\fi\number#3#1\relax
}%
}%
}{%
%Case someting else, e.g. v/V:
\ifnum\number\numexpr#2-#1\relax<#1 %
\expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi
{%
\@firstofone{\expandafter} \number\numexpr\ifnum\number#3#1<0 \else-\fi#2+\number#3#1\relax
}{%
\@firstofone{\expandafter} \number#3#1%
}%
}%
}%
}%
\newcommand{\pgcd}[2]{%
% #1 = first integer number
% #2 = second integer number
\romannumeral0%
\ifnum#1=0 \expandafter\@firstofone\else\expandafter\@secondoftwo\fi
{%
\ifnum#2=0 \expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi
{ \text{non-défini}}%
}%
{%
\expandafter\PassFirstToSecond\expandafter{%
\number\ifnum#2<0 -\fi\number#2%
}{%
\expandafter\PassFirstToSecond\expandafter{%
\number\ifnum#1<0 -\fi\number#1%
}{\pgcdloop}%
}%
}%
}%
\newcommand{\pgcdloop}[2]{%
\ifnum#2>0 \expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi
{%
\expandafter\PassFirstToSecond\expandafter{%
\romannumeral0\resteloop{#1}{#2}{}{}{p}%
}{\pgcdloop{#2}}%
}%
{ #1}%
}
\makeatother
\begin{document}
\footnotesize\parindent=0ex
Le reste avec la valeur absolue la plus petite dans la division entière de $0$ par $6$ est $\reste{v}{0}{6}$.\\
Le reste avec le meme signe que le module dans la division entière de $0$ par $6$ est $\reste{s}{0}{6}$.\\
Le reste avec valeur positive dans la division entière de $0$ par $6$ est $\reste{p}{0}{6}$.\\
Le reste avec la valeur absolue la plus petite dans la division entière de $6$ par $0$ est $\reste{v}{6}{0}$.\\
Le reste avec le meme signe que le module dans la division entière de $6$ par $0$ est $\reste{s}{6}{0}$.\\
Le reste avec valeur positive dans la division entière de $6$ par $0$ est $\reste{p}{6}{0}$.\\
Le reste avec la valeur absolue la plus petite dans la division entière de $29$ par $6$ est $\reste{v}{29}{6}$.\\
Le reste avec le meme signe que le module dans la division entière de $29$ par $6$ est $\reste{s}{29}{6}$.\\
Le reste avec valeur positive dans la division entière de $29$ par $6$ est $\reste{p}{29}{6}$.\\
Le reste avec la valeur absolue la plus petite dans la division entière de $29$ par $-6$ est $\reste{v}{29}{-6}$.\\
Le reste avec le meme signe que le module dans la division entière de $29$ par $-6$ est $\reste{s}{29}{-6}$.\\
Le reste avec valeur positive dans la division entière de $29$ par $-6$ est $\reste{p}{29}{-6}$.\\
Le reste avec la valeur absolue la plus petite dans la division entière de $-29$ par $6$ est $\reste{v}{-29}{6}$.\\
Le reste avec le meme signe que le module dans la division entière de $-29$ par $6$ est $\reste{s}{-29}{6}$.\\
Le reste avec valeur positive dans la division entière de $-29$ par $6$ est $\reste{p}{-29}{6}$.\\
Le reste avec la valeur absolue la plus petite dans la division entière de $-29$ par $-6$ est $\reste{v}{-29}{-6}$.\\
Le reste avec le meme signe que le module dans la division entière de $-29$ par $-6$ est $\reste{s}{-29}{-6}$.\\
Le reste avec valeur positive dans la division entière de $-29$ par $-6$ est $\reste{p}{-29}{-6}$.\\
Le reste avec la valeur absolue la plus petite dans la division entière de $30$ par $6$ est $\reste{v}{30}{6}$.\\
Le reste avec le meme signe que le module dans la division entière de $30$ par $6$ est $\reste{s}{30}{6}$.\\
Le reste avec valeur positive dans la division entière de $30$ par $6$ est $\reste{p}{30}{6}$.\\
Le reste avec la valeur absolue la plus petite dans la division entière de $30$ par $-6$ est $\reste{v}{30}{-6}$.\\
Le reste avec le meme signe que le module dans la division entière de $30$ par $-6$ est $\reste{s}{30}{-6}$.\\
Le reste avec valeur positive dans la division entière de $30$ par $-6$ est $\reste{p}{30}{-6}$.\\
Le reste avec la valeur absolue la plus petite dans la division entière de $0$ par $0$ est $\reste{v}{0}{0}$.\\
Le reste avec le meme signe que le module dans la division entière de $0$ par $0$ est $\reste{s}{0}{0}$.\\
Le reste avec valeur positive dans la division entière de $0$ par $0$ est $\reste{p}{0}{0}$.\\
Le plus grand commun diviseur de $29$ et $6$ est $\pgcd{29}{6}$.
Le plus grand commun diviseur de $0$ et $6290$ est $\pgcd{0}{6290}$.
Le plus grand commun diviseur de $6324$ et $0$ est $\pgcd{6324}{0}$.
Le plus grand commun diviseur de $0$ et $-6290$ est $\pgcd{0}{-6290}$.
Le plus grand commun diviseur de $-6324$ et $0$ est $\pgcd{-6324}{0}$.
Le plus grand commun diviseur de $6324$ et $6290$ est $\pgcd{6324}{6290}$.
Le plus grand commun diviseur de $-6324$ et $6290$ est $\pgcd{-6324}{6290}$.
Le plus grand commun diviseur de $6324$ et $-6290$ est $\pgcd{6324}{-6290}$.
Le plus grand commun diviseur de $-6324$ et $-6290$ est $\pgcd{-6324}{-6290}$.
Le plus grand commun diviseur de $0$ et $0$ est $\pgcd{0}{0}$.
\end{document}