为什么在此示例中使用 \@secondoftwo?

为什么在此示例中使用 \@secondoftwo?

我一直在研究这个代码TUGboat 文章由 Joseph Wright 和 Christian Feurersänger (第 30 卷,2009 年,第 1 期) 解释键值技术。我理解\expandafter\@firstofone前两个用户宏中的用法,但我不明白\@secondoftwo第三个用户宏中引用的第二个参数来自哪里。它是传递给选项的所需颜色的名称吗colour

我理解这里的扩展是如何工作的。我不明白为什么\@secondoftwo使用 而不是\@firstofone。问题是https://tex.stackexchange.com/a/21263/218142使得区别非常明显,但在这种情况下我看不出区别。

这是(我稍微编辑了一下) MWE 包文件:

\NeedsTeXFormat{LaTeX2e}
\ProvidesPackage{xmph}[2008/03/17 v1.0 Extended emph] 
\RequirePackage{color,kvoptions} 
\SetupKeyvalOptions{%
  family=xmph,
  prefix=xmph@
}%
\DeclareBoolOption{useitalic}
\DeclareBoolOption{usebold}
\DeclareBoolOption{usecolour}
\DeclareBoolOption{usecolor}
\let\KV@xmph@usecolor
  \KV@xmph@usecolour
\DeclareStringOption{colour}
\define@key{xmph}{color}{\setkeys{xmph}{colour=#1}}
\DeclareVoidOption{inactive}{%
  \PackageInfo{xmph}{Package inactive}%
  \AtEndOfPackage{\let\xmph\emph}%
}%
\setkeys{xmph}{useitalic,colour=red}
\ProcessKeyvalOptions{xmph}
\define@key{xmph}{inactive}{%
  \PackageInfo{xmph}{Package inactive}
  \let\xmph\emph
}%
\AtBeginDocument{%
  \DisableKeyvalOption[action=warning,package=xmph]{xmph}{inactive}
}%
\newcommand*{\xmphsetup}{%
  \setkeys{xmph}%
}%

\newcommand*{\xmph}[1]{%
  \xmph@emph{%
    \xmph@bold{%
      {%
        \xmph@colourtext{#1}
      }%
    }%
  }%
}%
\newcommand*{\xmph@emph}{%
  \ifxmph@useitalic 
    \expandafter\emph
  \else 
    \expandafter\@firstofone
  \fi
}%
\newcommand*{\xmph@bold}{%
  \ifxmph@usebold 
    \expandafter\textbf
  \else 
    \expandafter\@firstofone
  \fi
}%
\newcommand*{\xmph@colourtext}{%
  \ifxmph@usecolour 
    \expandafter\textcolor
  \else 
    \expandafter\@secondoftwo
  \fi
  {\xmph@colour}
}%

以下是使用包文件的 MWE 文档(再次经过稍微编辑):

% !TEX TS-program = lualatexmkjoe
% !TEX encoding = UTF-8 Unicode

\documentclass{article}
\usepackage[usecolour,color=blue,usebold]{xmph}
%\xmphsetup{inactive}

\begin{document}
Some text \xmph{text}
  
\xmphsetup{usecolor=false,usebold=false,useitalic=false}%
\xmph{more text}

\xmphsetup{usecolor=true,color=cyan,usebold=false,useitalic=true}%
\xmph{more text}
  
\xmphsetup{usecolor=false,usebold=false,useitalic=false}%
\xmph{even more text}
\end{document}

答案1

考虑一下这个调用\xmph{foo} abc。这意味着

\xmph@emph{\xmph@bold{{\xmph@colourtext{foo} }}} abc

根据您的代码。现在\xmph@emph被扩展。这变成了

\ifxmph@useitalic\expandafter\emph\else\expandafter\@firstofone\fi⮐
{\xmph@bold{{\xmph@colourtext{foo} }}} abc

(符号表示连续)。根据 的真值\ifxmph@useitalic,这变为

\emph{\xmph@bold{{\xmph@colourtext{foo} }}} abc

或者

\@firstofone{\xmph@bold{{\xmph@colourtext{foo} }}} abc

后者只是从参数中去掉括号,为了简单起见,我将遵循这种情况。我们得到

\xmph@bold{{\xmph@colourtext{foo} }} abc

然后变成

\ifxmph@usebold\expandafter\textbf\else\expandafter\@firstofone\fi⮐
\xmph@bold{{\xmph@colourtext{foo} }} abc

同样,根据的真值,\ifxmph@usebold我们可以得到

\textbf{{\xmph@colourtext{foo} }} abc

或者

\@firstofone{{\xmph@colourtext{foo} }} abc

让我们来看看后一种情况,得到

{\xmph@colourtext{foo} } abc

开括号被消化,打开一个组;然后我们得到

\ifxmph@usecolour\expandafter\textcolor\else\expandafter\@secondoftwo\fi⮐
{\xmph@colour} {foo} } abc

根据的值,\ifxmph@usecolour我们可以得到

\textcolor{\xmph@colour} {foo} } abc

或者

\@secondoftwo{\xmph@colour} {foo} } abc

现在你应该清楚为什么\@secondoftwo是必要的,所以颜色部分从输入流中删除。中间的空格按规则删除,因为\@secondoftwo需要两个未分隔的参数(\textcolor最终调用一个也需要两个参数的宏)。

现在foo打印(彩色或非彩色),打印以下空间,关闭已打开的组,打印另一个空间并从继续处理abc

\emph使用和的情况\textbf只会增加一些复杂性,但就理解而言,这并不相关\@secondoftwo

您会发现有两个点需要修复:虚假空间和无用组。尝试输入

\documentclass{article}
\usepackage[usecolour,color=blue,usebold]{xmph}
%\xmphsetup{inactive}

\begin{document}

Some text \emph{\textbf{\textcolor{blue}{text}}} some text

Some text \xmph{text} some text
  
\xmphsetup{usecolor=false,usebold=false,useitalic=false}

\xmph{more text} some text

\xmphsetup{usecolor=true,color=cyan,usebold=false,useitalic=true}

\xmph{more text} some text
  
\xmphsetup{usecolor=false,usebold=false,useitalic=false}

\xmph{even more text} some text

\end{document}

在此处输入图片描述

您将看到虚假的空间。修复丢失的%标记(并删除不需要的标记)并删除无用的组:

\NeedsTeXFormat{LaTeX2e}
\ProvidesPackage{xmph}[2008/03/17 v1.0 Extended emph] 
\RequirePackage{color,kvoptions} 
\SetupKeyvalOptions{%
  family=xmph,
  prefix=xmph@
}
\DeclareBoolOption{useitalic}
\DeclareBoolOption{usebold}
\DeclareBoolOption{usecolour}
\DeclareBoolOption{usecolor}
\let\KV@xmph@usecolor\KV@xmph@usecolour
\DeclareStringOption{colour}
\define@key{xmph}{color}{\setkeys{xmph}{colour=#1}}
\DeclareVoidOption{inactive}{%
  \PackageInfo{xmph}{Package inactive}%
  \AtEndOfPackage{\let\xmph\emph}%
}
\setkeys{xmph}{useitalic,colour=red}
\ProcessKeyvalOptions{xmph}
\define@key{xmph}{inactive}{%
  \PackageInfo{xmph}{Package inactive}%
  \let\xmph\emph
}
\AtBeginDocument{%
  \DisableKeyvalOption[action=warning,package=xmph]{xmph}{inactive}%
}
\newcommand*{\xmphsetup}{%
  \setkeys{xmph}%
}

\newcommand*{\xmph}[1]{%
  \xmph@emph{%
    \xmph@bold{%
      \xmph@colourtext{#1}%
    }%
  }%
}
\newcommand*{\xmph@emph}{%
  \ifxmph@useitalic 
    \expandafter\emph
  \else 
    \expandafter\@firstofone
  \fi
}
\newcommand*{\xmph@bold}{%
  \ifxmph@usebold 
    \expandafter\textbf
  \else 
    \expandafter\@firstofone
  \fi
}
\newcommand*{\xmph@colourtext}{%
  \ifxmph@usecolour 
    \expandafter\textcolor
  \else 
    \expandafter\@secondoftwo
  \fi
  {\xmph@colour}%
}

现在,与之前相同的输入文件产生

在此处输入图片描述


以下是使用 的实现expl3。没有\expandafter,编码更加直接。

\NeedsTeXFormat{LaTeX2e}
\ProvidesPackage{xmph}[2008/03/17 v1.0 Extended emph]
\RequirePackage{expl3,xparse,l3keys2e}
\RequirePackage{color}

\ExplSyntaxOn

%%% define the keys
\keys_define:nn { xmph }
 {
  useitalic .bool_set:N = \l_xmph_italic_bool,
  useitalic .default:n = true,
  usebold   .bool_set:N = \l_xmph_bold_bool,
  usebold   .default:n = true,
  usecolour .bool_set:N = \l_xmph_colour_bool,
  usecolour .default:n = true,
  usecolor  .bool_set:N = \l_xmph_colour_bool,
  usecolor  .default:n = true,
  colour    .tl_set:N = \l_xmph_colour_tl,
  color     .tl_set:N = \l_xmph_colour_tl,
  inactive  .code:n = \__xmph_inactivate:,
  inactive  .value_forbidden:n = true,
 }

% we don't want to set inactive in the document
\AtBeginDocument
 {
  \keys_define:nn { xmph }
   {
    inactive .code:n = \PackageWarning{xmph}{Disabled~option},
   }
 }

% process the package keys
\ProcessKeysPackageOptions { xmph }

%%% user level commands

\NewDocumentCommand{\xmphsetup}{m}
 {
  \keys_set:nn { xmph } { #1 }
 }

\NewDocumentCommand{\xmph}{m}
 {
  \xmph_emph:n { \xmph_bold:n { \xmph_colour:n { #1 } } }
 }

%%% internal functions

\cs_new_protected:Nn \__xmph_inactivate:
 {
  \PackageInfo{xmph}{Package~inactive}
  \AtBeginDocument{\cs_set_eq:NN \xmph \emph}
 }

\cs_new_protected:Nn \xmph_emph:n
 {
  \bool_if:NTF \l_xmph_italic_bool { \emph { #1 } } { #1 }
 }
\cs_new_protected:Nn \xmph_bold:n
 {
  \bool_if:NTF \l_xmph_bold_bool { \textbf { #1 } } { #1 }
 }
\cs_new_protected:Nn \xmph_colour:n
 {
  \bool_if:NTF \l_xmph_colour_bool { \textcolor { \l_xmph_colour_tl } { #1 } } { #1 }
 }

\ExplSyntaxOff

答案2

您应该遵循扩展以了解 的用法\@secondoftwo。考虑调用

\xmph{<stuff>}

\xmph根据其定义采用单个参数,并将其直接传递给\xmph@colourtext{#1}

现在,\xmph@colourtext定义为不带参数。因此,将usecolor选项设置为true

\ifxmph@usecolour 
  \expandafter\textcolor
\else 
  \expandafter\@secondoftwo
\fi

扩展为\textcolor。使用\usecolor=false,它将扩展为\@secondoftwo。在关闭之前,\xmph@colourtext将其插入{\xmph@colour}到输入线程中,有效地\xmph@colourtext{<stuff>}变成\textcolor{\xmph@colour}{<stuff>}\@secondoftwo{\xmph@colour}{<stuff>}

因此,\@secondoftwo跳过对颜色()的引用\xmph@colour并直接打印<stuff>

答案3

正如所写我的评论,并复制沃纳,这是为了跳过{\xmph@colour}。也就是说,您希望有效地获得 \textcolor{\xmph@colour} 或 ,而不想淹没在 \expandafters 中,因此您决定跳过 {\xmph@colour} ,如果您只想要 。

换句话说,当你打电话时

\xmph{<stuff>}

有两种情况。如果为\ifxmph@usecolour真,则得到

\textcolor{\xmph@colour}<stuff>

另一方面,如果\ifxmph@usecolour,则得到\@secondoftwo{\xmph@colour}{<stuff>},也就是{<stuff>}

相关内容