\def 的纯文本参数的行为与纯文本不同

\def 的纯文本参数的行为与纯文本不同

这是我的例子,与删除多余的花括号。除了我的应用程序之外,尝试在边距中对标签进行换行,即使它们中没有空格,我也试图了解发生了什么。

\documentclass{article}
\usepackage{showlabels}
\usepackage{seqsplit}
\usepackage{xpatch}
\makeatletter

% line-break annotations, http://tex.stackexchange.com/a/148613/30810
\patchcmd{\showlabelsetlabel}{#1}{\parbox[t]{\marginparwidth}{\myseqsplit{#1}}}{}{err}

% remove extra curly braces, http://tex.stackexchange.com/a/300871/30810
\newcommand{\myseqsplit}[1]{\expandafter\seqsplit\expandafter{\@firstofone#1}}

% remove curly braces from text
\patchcmd{\SL@margintext}{\{\SL@prlabelname{#1}\}}{#1}{}{err}

% testing patch: overwrite *definition* of \SL@prlabelname (twice because two code paths) - no effect
% \patchcmd{\SL@margtext}{\xdef\SL@labelname{\SL@prlabelname{#1}}}{\xdef\SL@labelname{kooooooooooooooooooooooooooooooong}}{}{err}
% \patchcmd{\SL@margtext}{\xdef\SL@labelname{\SL@prlabelname{#1}}}{\xdef\SL@labelname{kooooooooooooooooooooooooooooooong}}{}{err}

% testing patch: overwrite *use* of \SL@prlabelname - has an effect
% \patchcmd{\@eqnnum}{\SL@eqntext{\SL@labelname}}{\SL@eqntext{pooooooooooooooooooooooooooooooong}}{}{err}

% down the road, show the argument being passed. no change with either patch!
\pretocmd{\SL@eqnlrtext}{\edef\test{#1}\meaning\test}{}{err}

\begin{document}
    \begin{equation}
        \label{looooooooooooooooooooooooooooooong}
    \end{equation}
\end{document}

第一行应该相当明显,定义了parbox要放入标签的。第二行与我前面提到的问题有关,因为我假设额外的花括号可能是罪魁祸首(我不认为它们现在也是罪魁祸首)。第三行简化了中的文本,parbox使其成为一个单组参数,见上文。

现在,虽然整个过程在equation环境之外(不同的代码路径)确实有效,但在环境内部却不起作用,我试图调查原因。(这就是为什么这个 MWE 几乎归结为这个单一案例的原因。)

我的两个可选补丁覆盖了 的定义和使用\SL@labelname。有趣的是,虽然覆盖定义不起作用(它可以工作,但对换行符没有影响),但覆盖使用却有效。更有趣的是,后面的最后一个补丁显示了参数的值,而这个值在不同的补丁之间似乎没有变化(至少我看不出有什么区别)。

答案1

而且,它真的有效。太神奇了。

\seqsplit遍历除最后一步之外的每个步骤中插入的标记列表。

因此,如果给定looooong,则在构建循环时忽略扩展顺序和赋值的有效行为是

l\seqinsert o\seqinsert o\seqinsert o\seqinsert o\seqinsert o\seqinsert n\seqinsert g

如果提供的序列是单个标记\kong\seqsplit则不执行任何有用的操作,因为该序列的长度为 1。

解决这个问题的标准方法是\kong在调用之前\seqsplit进行扩展

\expandafter\seqsplit\expandafter{\kong}

如果\kong定义为,则looooong只需一步即可扩展为

\seqsplit{looooong}

从而产生与原始相同的结果。

建议使用

\seqsplit{\expandafter\kong}

其工作或多或少是偶然的,并且严重依赖于循环的具体实现。

稍微简化\seqsplit一下,通过查看第一个标记,检查它不是标记输入结束的保护标记,如果不是,则输出第一个标记,然后输出\SQSPL@insert递归开始查看下一个标记的命令。

因此

\seqsplit{\expandafter\kong}

第一个 token 是,\expandafter所以第一步是

\expandafter\SQSPL@insert\kong

但是现在这个第一个标记不仅仅是一个像l扩展一样的字符,所以\kong在循环递归之前扩展标记,所以下一步是

\SQSPL@insert looooong

从这一点开始,迭代继续进行,就好像字符串在输入参数中已经明确一样。

因此,该{\expandafter\kong}形式之所以有效,是因为\seqsplit迭代只会在标记之间放置一个标记,以便扩展\expandafter\kong,也因为它会导致\seqinsert在第一个标记之后插入,\expandafter这意味着最终结果不等同于显式输入,而是等同于

    \seqinsert l\seqinsert o\seqinsert o\seqinsert o\seqinsert o\seqinsert o\seqinsert n\seqinsert g

\seqinsert在开头有一个附加项。(您可以通过将其定义\seqinsert为可见的内容来看到这一点,例如

\def\seqinsert{!}

默认定义只是一个\hspace自然大小为 0 的,所以这个虚假空间不是那么明显,但如果你这样做

\def\seqinsert{\ifmmode\allowbreak\else\hspace{20pt plus 0.02em}\fi}

您会看到该{\expandafter\kong}版本有 10pt 的虚假缩进。

因此,要在宏调用之前扩展参数,

\expandafter\seqsplit\expandafter{\kong}

形式为佳。

答案2

将我的 MWE 降低到这个之后

\documentclass{article}
\usepackage{seqsplit}
\makeatletter
\newcommand{\myseqsplit}[1]{\expandafter\seqsplit\expandafter{\@firstofone#1}}
\begin{document}
    \seqsplit{loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong}

    \def\kong{loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong}
    \seqsplit{\kong}
\end{document}

我试过\seqsplit{\expandafter\kong}

而且,它真的有效。太神奇了。

因此,在我最初的 MWE 中,情况是这样的:

\patchcmd{\@eqnnum}{\SL@eqntext{\SL@labelname}}{\SL@eqntext{\expandafter\SL@labelname}}{}{err}

如果你使用amsmath,这可能会更完整:

\ifSL@AMS
    \patchcmd{\maketag@@@}{{\df@label}}{{\expandafter\df@label}}{}{err}
    \patchcmd{\maketag@@@}{{\SL@labelname}}{{\expandafter\SL@labelname}}{}{err}
\else
    \patchcmd{\@eqnnum}{{\SL@labelname}}{{\expandafter\SL@labelname}}{}{err}
\fi

相关内容