我一直在研究这个代码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>}
。