我想要一个宏来\ignorepars
删除\par
其后的所有 s。我写了以下代码:
\def\eat{}
\def\ignorepars{\futurelet\next\ignoreparsA}
\def\ignoreparsA{\ifx\next\par\expandafter\ignorepars\eat\fi}
但它不起作用(它会导致 TeX 容量超出错误)。例如,假设我有\ignorepars\par x
。然后,根据我的说法,扩展\ignorepars
使得\next
类似于\par
并且标记列表变为\ignoreparsA\par x
;接下来扩展后\ignoreparsA
它保持为\expandafter\ignorepars\eat\par x
;因此,在另一次扩展之后,标记列表变为\ignorepars x
;这使得\next
具有值 x 并扩展为\ignoreparsA x
;由于谓词为假,因此这会扩展为空\ifx
。
但实际上发生了什么?
编辑: \def\eat{}
应该是\long\def\eat#1{}
(这是一个打字错误)
答案1
我们慢慢来吧:
\ignorepars\par x
变成
\futurelet\next\ignoreparsA\par x
现在\next
是\let
到\par
并且\ignoreparsA
是扩展的,所以我们有
\ifx\next\par\expandafter\ignorepars\eat\fi\par x
条件为真,所以\ifx\next\par
消失,留下
\expandafter\ignorepars\eat\fi\par x
现在\expandafter
展开\eat
,其展开式为空,所以我们有
\ignorepars\fi\par x
OK,\ignorepars
展开如下:
\futurelet\next\ignoreparsA\fi\par x
现在\next
是\let
并且\fi
正在\ignoreparsA
扩大
\ifx\next\par\expandafter\ignorepars\eat\fi\fi\par x
条件为假,所以我们得到
\fi\par x
最后,的扩展\fi
为空。因此,如您所见,没有\par
被吞掉。事实上,测试文件
\def\eat{}
\def\ignorepars{\futurelet\next\ignoreparsA}
\def\ignoreparsA{\ifx\next\par\expandafter\ignorepars\eat\fi}
x\ignorepars\par x
\bye
不会导致 TeX 容量超出,但会打印两个段落。
如果你的定义\eat
是
\def\eat#1{}
那么你确实会陷入无限循环,因为\eat
会一次又一次地吞噬\fi
并呈现 TeX 。\ignorepars\par
你有更好的运气
\long\def\eat#1{\ignorepars}
\def\ignorepars{\futurelet\next\ignoreparsA}
\def\ignoreparsA{\ifx\next\par\expandafter\eat\fi}
x\ignorepars\par x
\bye
摆脱\expandafter
,\fi
而\eat
吞噬\par
令牌和供应\ignorepars
。
测试文件
\long\def\eat#1{\ignorepars}
\def\ignorepars{\futurelet\next\ignoreparsA}
\def\ignoreparsA{\ifx\next\par\expandafter\eat\fi}
x\ignorepars\par x
y\ignorepars
y
z\ignorepars
z
\bye
答案2
这比 egreg 的建议少了很多技巧,但有时蛮力是有效的。
\let\oldpar\par
\def\ignorepar{\let\par\ignorespaces}
\def\restorepar{\endgraf\let\par\oldpar}
\def\bye{\endgraf\end}
\ignorepar
abcdef ghijkl mnopqr stuv wxyz
abcdef ghijkl mnopqr stuv wxyz
abcdef ghijkl mnopqr stuv wxyz
abcdef ghijkl mnopqr stuv wxyz
\restorepar
abcdef ghijkl mnopqr stuv wxyz
abcdef ghijkl mnopqr stuv wxyz
\ignorepar
abcdef ghijkl mnopqr stuv wxyz
abcdef ghijkl mnopqr stuv wxyz
abcdef ghijkl mnopqr stuv wxyz
abcdef ghijkl mnopqr stuv wxyz
\restorepar
abcdef ghijkl mnopqr stuv wxyz
abcdef ghijkl mnopqr stuv wxyz
\bye
答案3
空格需要特殊处理才能被忽略,因为它们是字符标记,但是\par
是一个 csname,所以您可以更简单地执行 \let\par\empty
(例如,latex 在中执行的操作tabular
)(如果以这种方式被忽略,显然您需要一些钩子(例如组末尾)来恢复定义\par
。)