我正在研究宏,作为练习,我想计算一个数字的后继,以文本表示,二进制,小端序(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
产生,产生。1
0
\go1\relax
01
无论如何,我不明白为什么\go01\relax
会产生一个空的输出。手动遵循扩展甚至替换其片段都会给出正确的结果11
。我遗漏了什么?
答案1
我猜你的数字是大端字节序,因为它们以最高有效位结束(从书写的意义上来说)。
让我们看看会发生什么\go01\relax
。的参数\go
是0
,所以我们得到
\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\toks0
TeX 需要知道所表示的 -register 的数量\toks
,因此收集(并由此扩展可扩展的)形成 TeX 的标记⟨数字⟩-数量。因此,在遇到数字后,0
TeX 不会吐出寄存器的内容\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