LaTeX 的 \@tfor 循环

LaTeX 的 \@tfor 循环

这是一个有趣的案例。请问为什么案例 1 失败了?

\def\expandsecond#1#2{\begingroup\edef\x{\endgroup\unexpanded{#1}#2}\x}  
\def\maybe@ic@{%
  \ifdim\fontdimen\@ne\font>\z@\else
    \maybe@ictrue
    % Case 1:
    \expandsecond{\@tfor\reserved@a:=}\nocorrlist\do{\t@st@ic}%
    % Case 2 (original):
    %\expandafter\@tfor\expandafter\reserved@a\expandafter:%
    % \expandafter=\nocorrlist\do{\t@st@ic}%
    \ifmaybe@ic\sw@slant\fi
  \fi
}
\def\t@st@ic{%
  \expandafter\let\expandafter\reserved@b\expandafter=\reserved@a\relax
  \ifx\reserved@b\@let@token
    \maybe@icfalse
    \@break@tfor
  \fi
}

错误来自

\textit{foof}

错误:

! Incomplete \iffalse; all text was ignored after line 163.
<inserted text>
                \fi
<*> italic-correction.tex

通过以下修改,我遇到了同样有趣的问题:

\def\newtfor#1:={\new@tfor#1 }
\long\def\new@tfor#1#2\do#3{%
  \edef\newfortmp{\unexpanded{#2}}%
  \ifx\newfortmp\space\expandafter\@gobble\else\expandafter\@iden\fi
  {\newtforloop#2\@nil\@nil\newtfor#1{#3}}%
} 
\long\def\newtforloop#1#2\newtfor#3#4{%
  \edef#3{\unexpanded{#1}}%
  \ifx#3\@nnil\expandafter\@gobble\else\expandafter\@iden\fi
  {#4\relax\newtforloop#2\newtfor#3{#4}}%
}
\long\def\breaknewtfor#1\newtfor#2#3{}

\def\maybe@ic@{%
  \ifdim\fontdimen\@ne\font>\z@\else
    \maybe@ictrue
    % Case 1:
    %\expandsecond{\newtfor\reserved@a:=}\nocorrlist\do{\t@st@ic}%
    % Case 2:
    \expandafter\newtfor\expandafter\reserved@a\expandafter
      :\expandafter=\nocorrlist\do{\t@st@ic}%
    \ifmaybe@ic\sw@slant\fi
  \fi
}
\def\t@st@ic{%
  \expandafter\let\expandafter\reserved@b\expandafter=\reserved@a\relax
  \ifx\reserved@b\@let@token
    \maybe@icfalse
    \expandafter\breaknewtfor
  \fi
}

编辑(2012/08/13)

以下是 M(N)WE:

\documentclass{article}
\usepackage{xspace}

\edef\x{catcode of comma=\the\catcode`\,\space,
  \space catcode of stop=\the\catcode`\.}
%\show\x
%\show\nocorrlist

\makeatletter
\def\expandsecond#1#2{%
  \begingroup\edef\x{\endgroup\unexpanded{#1}#2}\x
}
%\def\nocorrlist{.,\xspace} % source of the problem.
\def\maybe@ic{\futurelet\@let@token\maybe@ic@}
\def\maybe@ic@{%
  \ifdim\fontdimen\@ne\font>\z@\else
    \maybe@ictrue
    % Case 1:
    \expandsecond{\@tfor\reserved@a:=}\nocorrlist\do{\t@st@ic}%
    % Case 2:
    %\expandafter\@tfor\expandafter\reserved@a\expandafter
    %  :\expandafter=\nocorrlist\do{\t@st@ic}%
    \ifmaybe@ic\sw@slant\fi
  \fi
}
\makeatother

\begin{document}
\textit{foof}. \textit{foof}!
\end{document}

该问题是由我加载的一个包引起的:它确实如此\def\nocorrlist{.,\xspace}

答案1

正如 David Carlisle 所说,不要完全扩展\nocorrlist。LaTeX 的斜体校正代码使用\futurelet(在宏中\maybe@ic)查找下一个标记。如果下一个标记是列表的元素\nocorrlist,则斜体校正将被抑制。LaTeX 的原始定义仅将逗号和句点作为 catcode 为 12(其他)的标记。但用户可以通过其他包扩展列表(名称中没有@)。扩展的标记也可以是宏,请参阅包biblatex

\appto\nocorrlist{\isdot\adddot\addperiod\addcomma} 

我假设的目的\expandsecond是在第一个参数之后扩展一个标记,以提供扩展的列表\@tfor。它的行为类似于\expandafter,不同之处在于它跳过一个标记列表而不是单个标记。插入的组使宏形式不可扩展。以下定义\expandsecond扩展标记一次,而不是完全扩展。并且它不需要 e-TeX 的\unexpanded

\def\expandsecond#1{%
  \begingroup
  \long\def\x{\endgroup
    #1
  }%
  \expandafter\x
}

可以通过使用宏来避免该组:

\def\expandsecond#1{%
  \long\def\expandsecond@next{#1}%
  \expandafter\expandsecond@next
}

然而,由于宏定义,该定义仍然不可扩展。

如果\newtfor我不知道,你想改进什么。由于额外的宏扩展步骤( \@iden`),重新定义的效率可能会稍微低一些\@gobble,。两种变体都可以处理标记列表中不匹配的条件。

对以下几行的注释

\edef\newfortmp{\unexpanded{#2}}%
\edef#3{\unexpanded{#1}}%

的目的\unexpanded是为了防止扩张,因此最终的结果是(除了#,参见“编辑”):

\def\newfortmp{#2}%
\def#3{#1}%

\@tfor就像LaTeX 中循环的原始定义一样。

编辑:Joseph Wright 指出有一个例外。catcode 为 6 的标记(参数),通常是哈希字符#,需要加倍,而无需\edef + \unexpanded

相关内容