\begingroup\def 技巧与宏分隔符与条件

\begingroup\def 技巧与宏分隔符与条件

在这个 MWE 中,组合newtxtextmicrotype会导致编译错误(Extra \else)。这个特定的例子是受svjour3document 类启发的,但我将其简化为article类和对的简单重新定义\normalsize。您可能会认为\iftrue ... \else\fi构造与相比不应该有太大变化...,但事实确实如此:

\documentclass{article}
\let\oldnormalsize\normalsize

% this works!
%\let\normalsize\oldnormalsize

% this works!
%\renewcommand\normalsize{\oldnormalsize}

% this works!
%\renewcommand\normalsize{\iffalse\else\fi\oldnormalsize}

% inspired by svglov3.clo. Does not work!
%\renewcommand\normalsize{\iffalse\else\oldnormalsize\fi}

% Does not work, either!
\renewcommand\normalsize{\iftrue\oldnormalsize\else\fi}

\usepackage{newtxtext}
\usepackage{microtype}
\begin{document}
    Anything
\end{document}

这是newtxtext或中的一个错误microtype,还是svjour3开发人员正在做一些应该避免的事情?我真的看不出那可能是什么。

microtype顺便说一句,颠倒和的顺序newtxtext可以消除该错误。我没有找到任何关于该主题的有用信息,但我想知道是否microtype应该在所有字体包都加载后才加载。快速测试表明,在我的较长文档中,这两个包的顺序无关紧要。

问题可以归结于这个 MWE(详情请参阅我的回答,我不想让这个问题泛滥)。删除三个不需要的iftrue,\iffalse结构中的任何一个也可以解决问题,重新排序\ifs中的也可以解决问题\ShowError。(我只能用.\iffalse包围来重现这个问题\iftrue

\documentclass{article}
\makeatletter

\newcommand{\CommandOne}[3]{#1,#2,#3}
\newcommand{\CommandTwo}{\iftrue\CommandOne{1}{2}{3}\else\fi}
% this works:
% \renewcommand{\CommandTwo}{\CommandOne{1}{2}{3}}

\def\ShowError{
    \iftrue
        \iffalse
        \else
            \begingroup
                \def\CommandOne##1##2##3\@nil{\endgroup}
                \CommandTwo\@nil
        \fi
    \else
    \fi
}
\ShowError

\begin{document}
    X
\end{document}

答案1

仅使用您已经生成的小型 MWE,这一点就变得很容易解释:

首先让我们看一下这导致的上下文。以下只是这导致的内容,我将每个宏替换为其替换文本,并缩进 6 个额外空格(如果宏位于 -block 内,则会\if导致 8 个,因为我将它们缩进 2 个空格):

\iftrue
        \iffalse
        \else
                \iffalse
                \else
                        \begingroup
                        \def\set@fontsize#1\@nil{\endgroup}%
                              \iftrue
                                \normalsize
                              \else
                              \fi
                        \@nil
                \fi
        \fi
\else
\fi

现在\normalsize扩展到一些检查然后\set@fontsize,所以如果我们忽略检查(以及通常的参数,因为\set@fontsize它们在这里并不重要)这将变成:

\iftrue
        \iffalse
        \else
                \iffalse
                \else
                        \begingroup
                        \def\set@fontsize#1\@nil{\endgroup}%
                              \iftrue
                                      \set@fontsize
                              \else
                              \fi
                        \@nil
                \fi
        \fi
\else
\fi

如果我们现在仅扩展的临时定义\set@fontsize而不评估之前的所有\ifs 和\elses,它将删除直到和在内的所有内容\@nil,而是留下一个\endgroup,因此我们得到:

\iftrue
        \iffalse
        \else
                \iffalse
                \else
                        \begingroup
                        \def\set@fontsize#1\@nil{\endgroup}%
                              \iftrue
                                      \endgroup
                \fi
        \fi
\else
\fi

现在让我们删除的内容,\begingroup...\endgroup因为除了包含的内容之外,它们不再有趣\iftrue

\iftrue
        \iffalse
        \else
                \iffalse
                \else
                              \iftrue
                \fi
        \fi
\else
\fi

现在问题变得明显了。让我们重新缩进一下,让它更易读:

\iftrue
  \iffalse
  \else
    \iffalse
    \else
      \iftrue
      \fi
    \fi
  \else
  \fi

接下来,我们将删除匹配的块对\if...\else...\fi,首先是最内层的\iftrue

\iftrue
  \iffalse
  \else
    \iffalse
    \else
    \fi
  \else
  \fi

现在最里面的\iffalse

\iftrue
  \iffalse
  \else
  \else
  \fi

我们到达了Extra \else

问题是,您的临时\set@fontsize定义将删除\if构造的重要部分,此后您得到的结果将是不平衡的——\if指令。\else\fi

答案2

我将在这里总结我从 @egreg 的回答开始发现的内容。下面的 MWE 是我可以将其简化为的,独立于svjour3newtxtextmicrotype通过繁琐地遵循执行函数的顺序。我最终不明白的是\MT@get@size中的这个microtype,它看起来像这样:

\def\MT@get@size{%
  ...
  \ifx\@tempa\relax \else
    \begingroup
      \def\set@fontsize##1##2##3##4\@nil{\endgroup\def\MT@val{##2}}%
      \@tempa\@nil
  \fi
  ...
}

这对我来说看起来很糟糕:我不知道为什么\begingroup/\endgroup{,}在那里交错出现。(结果是这样的:关于 \begingroup\edef\x{\endgroup

因此从某种意义上说,keyval不喜欢将这个\begingroup技巧与 结合起来\iftrue ... \else\fi,但我无法进一步隔离这一点。

\documentclass{article}
\usepackage{keyval}

\newcommand{\fancysize}{\iftrue\normalsize\else\fi}

\makeatletter
\define@key{foo}{bar}[]{
    \begingroup
        \def\set@fontsize##1##2##3##4\@nil{\endgroup}
        \fancysize
        \@nil
    }
\setkeys{foo}{bar}

\begin{document}
    X
\end{document}

有趣的是,删除\@nil有效:

\define@key{foo}{bar}[]{
    \begingroup
    \def\set@fontsize##1##2##3##4{\endgroup}
    \fancysize
}

这些是做什么\@nil用的?(答案:宏分隔符

此外,还在工程\@nil内部进行\iftrue...\else\fi

\define@key{foo}{bar}[]{
    \begingroup
    \def\set@fontsize##1##2##3##4\@nil{\endgroup}
    \iftrue\normalsize\@nil\else\fi
}

这表明在非工作情况下,\normalsize\else\fi\@nil变为...\set@fontsize...\else\fi\@nil,因此\else\fi成为的参数\set@fontsize,它们不匹配任何内容。这实际上是\begingroup\def技巧、宏分隔符和条件表达式的组合。所有这些都与大部分无关keyval

\documentclass{article}
\makeatletter

% similar to svjour3:
\newcommand{\fancysize}{\iftrue\normalsize\else\fi}

% isolated from microtype and keyval
\def\KV@foo@bar@default{
    \begingroup
        \def\set@fontsize##1##2##3##4\@nil{\endgroup}
        \fancysize
        \@nil
    }

% isolated from keyval
\def\KV@default{
    \iffalse\else
        \KV@foo@bar@default
    \fi
}

\def\KV@split{
    \iftrue
        \KV@default
    \else\fi
}

\KV@foo@bar@default % works!
\KV@default % works!
\KV@split % fails!

\begin{document}
    X
\end{document}

我想知道这是否能为人们提供在一般情况下如何解决这一问题的线索......

% \documentclass{svjour3}

% \usepackage{newtxtext}
% \usepackage[config=foo]{microtype}

% \DeclareMicrotypeSet{}{size={normalsize}}

% \MT@DeclareSet{}{size={normalsize}}

% \renewcommand\MT@DeclareSet[3][]{\MT@map@clist@c\MT@features{{\MT@declare@sets{##1}{#2}{#3}}}}
% \MT@DeclareSet{}{size={normalsize}}

% \MT@map@clist@c\MT@features{{\MT@declare@sets{#1}{}{size={normalsize}}}}

% \MT@map@clist@c\MT@features{{\MT@declare@sets{#1}{}{size={normalsize}}}}

% \MT@exp@one@n\MT@map@clist@n\MT@features{{\MT@declare@sets{#1}{}{size={normalsize}}}}

% \MT@exp@one@n\MT@map@clist@n\MT@features{{\MT@declare@sets{#1}{}{size={normalsize}}}}

% \expandafter\MT@map@clist@n\expandafter{pr,ex,sp,kn,tr}{{\MT@declare@sets{#1}{}{size={normalsize}}}}

% \def\MT@map@clist@n#1#2{\def\MT@clist@function##1{#2}\MT@map@clist@#1,\@nil,\@nnil}
% \expandafter\MT@map@clist@n\expandafter{pr,ex,sp,kn,tr}{{\MT@declare@sets{#1}{}{size={normalsize}}}}

% \def\MT@clist@function#1{{\MT@declare@sets{#1}{}{size={normalsize}}}}
% \MT@map@clist@ pr,ex,sp,kn,tr,\@nil,\@nnil

% \def\MT@clist@function#1{{\MT@declare@sets{#1}{}{size={normalsize}}}}
% \MT@map@clist@ pr,\@nil,\@nnil

% \def\MT@clist@function#1{{\MT@declare@sets{#1}{}{size={normalsize}}}}
% \MT@clist@function{pr}

% \MT@declare@sets{pr}{}{size={normalsize}}

% \def\MT@declare@sets#1#2#3{\setkeys{MT@#1@set}{#3}}
% \MT@declare@sets{pr}{}{size={normalsize}}

% \setkeys{MT@pr@set}{size={normalsize}}

% \define@key{foo}{size}[]{\MT@map@clist@n{#1}{\def\MT@val{##1}\expandafter\MT@get@range\MT@val--\@nil}}
% \setkeys{foo}{size={normalsize}}

% \define@key{foo}{size}{\MT@map@clist@n{#1}{\def\MT@val{##1}\expandafter\MT@get@range\MT@val--\@nil}}
% \setkeys{foo}{size={normalsize}}

% \define@key{foo}{size}{\def\MT@val{normalsize}\expandafter\MT@get@range\MT@val--\@nil}
% \setkeys{foo}{size={normalsize}}

% \define@key{foo}{bar}{\def\MT@val{normalsize}\expandafter\MT@get@range\MT@val--\@nil}
% \setkeys{foo}{bar=foobar}

% \define@key{foo}{bar}[]{\MT@get@range normalsize--\@nil}
% \setkeys{foo}{bar}

% \def\MT@get@range#1{\def\MT@val{#1}\MT@get@size}
% \define@key{foo}{bar}[]{\MT@get@range{normalsize}}
% \setkeys{foo}{bar}

% \define@key{foo}{bar}[]{\def\MT@val{normalsize}\MT@get@size}
% \setkeys{foo}{bar}

% \define@key{foo}{bar}[]{\MT@let@cn\@tempa{normalsize}\iffalse\else\begingroup\def\set@fontsize##1##2##3##4\@nil{\endgroup\def\MT@val{##2}}\@tempa\@nil\fi}
% \setkeys{foo}{bar}

相关内容