使用宏进行计算练习:扩展未按预期工作

使用宏进行计算练习:扩展未按预期工作

我正在研究宏,作为练习,我想计算一个数字的后继,以文本表示,二进制,小端序(lsb 优先)。经过巨大的努力,我设计了这个:

\def\finish#1\relax{#1}
\def\go#1{\toks0={}%
  \ifx#1\relax\toks0={1}\fi%
  \if0#1\toks0={1\finish}\fi%
  \if1#1\toks0={0\go}\fi%
  \the\toks0}

这个想法是从左边扫描数字并应用明显的变换,在需要时重复,否则复制其余的数字。我使用\relax作为字符串结尾的标记:我使用的是\end,从 TeXbook 中汲取灵感,但它在这里不起作用。

它在第一种情况下可以正常工作:\go\relax和作为 的后继\go0\relax产生,产生。10\go1\relax01

无论如何,我不明白为什么\go01\relax会产生一个空的输出。手动遵循扩展甚至替换其片段都会给出正确的结果11。我遗漏了什么?

答案1

我猜你的数字是大端字节序,因为它们以最高有效位结束(从书写的意义上来说)。

让我们看看会发生什么\go01\relax。的参数\go0,所以我们得到

\toks0={}\ifx0\relax\toks0={1}\fi\if00\toks0={1\finish}\fi\if10\toks0={0\go}\fi\the\toks01\relax

现在尝试

\def\finish#1\relax{#1}
\def\go#1{\toks0={}%
  \ifx#1\relax\toks0={1}\fi
  \if0#1\toks0={1\finish}\fi
  \if1#1\toks0={0\go}\fi
  \the\toks0%
}

\toks1={Oops!}

\go01\relax

\bye

并检查 TeX 是否打印“Oops!”。

你知道问题是什么了吗?这是一个(可治愈的)病例缺失空间综合症比它的同伴更糟糕一点虚假空间综合症. 解决方法如下:数字常量后始终留一个空格(TeXbook,第 208 页)。

\def\finish#1\relax{#1}
\def\go#1{\toks0={}%
  \ifx#1\relax\toks0={1}\fi
  \if0#1\toks0={1\finish}\fi
  \if1#1\toks0={0\go}\fi
  \the\toks0
}

0: \go0\relax

1: \go1\relax

01: \go01\relax

11: \go11\relax

011: \go011\relax

111: \go111\relax

\bye

在此处输入图片描述

您可能会喜欢看这个完全可扩展的版本。

\long\def\firstoftwo#1#2{#1}
\long\def\secondoftwo#1#2{#2}

\def\finish#1\relax{#1}
\def\go#1{%
  \ifx#1\relax
    \expandafter\firstoftwo
  \else
    \expandafter\secondoftwo
  \fi
  {1}{\digit#1}%
}
\def\digit#1{%
  \if0#1%
    \expandafter\firstoftwo
  \else
    \expandafter\secondoftwo
  \fi
  {1\finish}{0\go}%
}

0: \go0\relax

1: \go1\relax

01: \go01\relax

11: \go11\relax

011: \go011\relax

111: \go111\relax

\edef\test{\go10011101\relax}

{\tt\meaning\test}

\bye

答案2

这是您的任务的另一种实现,由于没有对 toks 寄存器进行设置,因此它更简单且完全可扩展。

如果您第一次这样做,那么您将不会遇到任何与注册有关的问题\toks01

\def\go#1{%
   \ifx#1\relax 1\expandafter\goF \fi
   \ifx#101\expandafter\goC
   \else  0\expandafter\go  % 1 converted to 0, do recursive \go
   \fi
}
\def\goF #1\go\fi{}   % \go Final, \relax scanned, 1 added, do nothing
\def\goC #1\relax{#1} % \go Copy, 0 converted to 1, copy rest

0: \go0\relax

1: \go1\relax

01: \go01\relax

11: \go11\relax

011: \go011\relax

111:  \go111\relax

11100:  \go11100\relax

1100110011:  \go1100110011\relax

000:  \go000\relax

\bye

我们可以将\go宏与\revers宏结合起来,创建\addone读取和打印二进制数的宏,就像我们习惯的那样:

\def\afterfi#1#2\fi{\fi#1}
\def\revers#1#2\relax{\ifx\relax#2\relax\else\afterfi{\revers#2\relax}\fi#1}

\def\go#1{%
   \ifx#1\relax 1\expandafter\goF \fi
   \ifx#101\expandafter\goC
   \else  0\expandafter\go  % 1 converted to 0, do recursive \go
   \fi
}
\def\goF #1\go\fi{}   % \go Final, \relax scanned, do nothing
\def\goC #1\relax{#1} % \go Copy, 0 converted to 1, copy rest.

\def\addone#1{%
   \expandafter\revers \expanded{%
       \expandafter\go\expanded{\revers#1\relax}\relax}%
   \relax
}

0: \addone{0}    % prints 1

1: \addone{1}    % prints 10

10: \addone{10}  % prints 11

11: \addone{11}  % prints 100

\bye

答案3

正如 egreg 已经指出的那样,使用指令\the\toks0TeX 需要知道所表示的 -register 的数量\toks,因此收集(并由此扩展可扩展的)形成 TeX 的标记⟨数字⟩-数量。因此,在遇到数字后,0TeX 不会吐出寄存器的内容\toks0,而是继续收集更多属于表示 -寄存器的数字的数字\toks,从而消耗属于 参数的标记\go
您可以通过 来阻止这种情况\the\toks0,即通过 。\the\toks0⟨space⟩

这是二进制数 Big-Endian-Incrementing 的另一种完全可扩展的变体,它不需要\if..-forking —而是通过分隔参数的分隔符匹配来完成分叉:

\def\go{\innergo{1}{0}{1}}
\def\innergo#1#2#3#4{%
    % #1 replacement for bit 0 
    % #2 replacement for bit 1
    % #3 digit to append when \relax is reached.
    % #4 current bit or \relax
  \ForkBit
  !#4!1!\relax!{#1\innergo{0}{1}{}}%      <- #4 (current bit) is 0
  !0!#4!\relax!{#2\innergo{#1}{#2}{#3}}%  <- #4 (current bit) is 1
  !0!1!#4!{#3}%                           <- #4 (current bit) is \relax
  !!!%
}%
\def\ForkBit#1!0!1!\relax!#2#3!!!{#2}%

101:  \go101\relax

001:  \go001\relax

111:  \go111\relax

11100:  \go11100\relax

1100110011:  \go1100110011\relax

00110011001100:  \go00110011001100\relax

000:  \go000\relax

\bye

在此处输入图片描述

如果您希望从 Big-Endian-Number 中删除尾随(有效)零,您可以尝试以下方法:

\def\go{\innergo{1}{0}{}{}}
\def\innergo#1#2#3#4#5{%
    % #1 replacement for bit 0 / digit to append when \relax is reached.
    % #2 replacement for bit 1
    % #3 symbol to gather when encountering bit 0
    % #4 replacements for bit 0 gathered so far
    % #5 current bit or \relax
  \ForkBit
  !#5!1!\relax!{#1\innergo{}{1}{0}{#4#3}}%    <- #5 (current bit) is 0
  !0!#5!\relax!{#4#2\innergo{#1}{#2}{#3}{}}%  <- #5 (current bit) is 1
  !0!1!#5!{#1}%                               <- #5 (current bit) is \relax
  !!!%
}%
\def\ForkBit#1!0!1!\relax!#2#3!!!{#2}%

101:  \go101\relax

001:  \go001\relax

111:  \go111\relax

11100:  \go11100\relax

1100110011:  \go1100110011\relax

00110011001100:  \go00110011001100\relax

000:  \go000\relax

\bye

在此处输入图片描述

相关内容