我目前正在写一份文档,有时需要我改变句子中字母的颜色。我以为找到一种方法来创建一个宏来实现这一点很容易,但我遇到了一些问题。
梅威瑟:
% !TeX program = xelatex
% !TeX spellcheck = en_US
\documentclass[11pt,a4paper,english,twoside,notitlepage]{book}
\usepackage{fontspec}
\usepackage{lmodern}
\usepackage[english,main=english]{babel}
\usepackage{xcolor}
\usepackage{etoolbox}
\definecolor{purple}{HTML}{2B0057}
\definecolor{candy}{HTML}{FF0000}
\newcounter{alternate}
%the macro in question
\newcommand{\colset}[1]{%
\ifnum\value{alternate}=0 {\color{candy}{#1}\setcounter{alternate}{1}}%
\else{\color{purple}{#1}\setcounter{alternate}{0}}%
\fi}
%snippet for the loop taken from https://tex.stackexchange.com/questions/359189/looping-over-strings
%iterates over the supplied string and replaces every letter with \colset{<letter>}
\def\gobblechar{\let\xchar= }
\def\assignthencheck{\afterassignment\xloop\gobblechar}
\def\xloop{%
\ifx\relax\xchar
\let\next=\relax
\else
\colset{\xchar}\let\next=\assignthencheck
\fi
\next}
\def\markletters#1{\setcounter{alternate}{0}\assignthencheck#1\relax}
\begin{document}
\markletters{Hello World}
\end{document}
问题是我找不到跳过空格的方法(例如,“World”中的“W”应该是紫色,而不是红色)。我以为只需检查参数是否为空格并跳过其余的宏就很容易了,但经过几个小时的搜索和反复试验(方法包括 etoolbox 的\ifblank{#1}
、类似\ifx{#1}{ }
或甚至的构造\ifnum`#1=32
、创建临时宏 [例如\edef\temp{#1}\ifblank{\temp}
])我仍然没有更进一步,而且我在这方面绝对力不从心。
有没有办法检查宏的参数是否只是一个空格?如果没有,还有其他方法可以构造这个宏吗?(我正在通过 MiKTeX 使用 XeLaTeX)
谢谢!
答案1
您没有使用宏参数进行迭代,因此您不需要测试宏参数,只需测试您已有的令牌:
\documentclass[11pt,a4paper,english,twoside,notitlepage]{book}
\usepackage{fontspec}
\usepackage{lmodern}
\usepackage[english,main=english]{babel}
\usepackage{xcolor}
\usepackage{etoolbox}
\definecolor{purple}{HTML}{2B0057}
\definecolor{candy}{HTML}{FF0000}
\newcounter{alternate}
%the macro in question
\newcommand{\colset}[1]{%
\ifnum\value{alternate}=0 {\color{candy}{#1}\setcounter{alternate}{1}}%
\else{\color{purple}{#1}\setcounter{alternate}{0}}%
\fi}
\makeatletter
%snippet for the loop taken from https://tex.stackexchange.com/questions/359189/looping-over-strings
%iterates over the supplied string and replaces every letter with \colset{<letter>}
\def\gobblechar{\let\xchar= }
\def\assignthencheck{\afterassignment\xloop\gobblechar}
\def\xloop{%
\ifx\relax\xchar
\let\next=\relax
\else
\ifx\@sptoken\xchar\setcounter{alternate}{\numexpr1-\value{alternate}}\fi
\colset{\xchar}\let\next=\assignthencheck
\fi
\next}
\makeatother
\def\markletters#1{\setcounter{alternate}{0}\assignthencheck#1\relax}
\begin{document}
\markletters{Hello World}
\end{document}
答案2
你可以用以下东西代替空格扩展到一個空間。
\documentclass[11pt,a4paper,english,twoside,notitlepage]{book}
\usepackage{fontspec}
\usepackage{lmodern}
\usepackage[english,main=english]{babel}
\usepackage{xcolor}
\definecolor{purple}{HTML}{2B0057}
\definecolor{candy}{HTML}{FF0000}
\ExplSyntaxOn
\NewDocumentCommand{\markletters}{m}
{
\int_zero:N \l_tmpa_int
\tl_set:Nn \l_tmpa_tl { #1 }
% replace spaces with something different
\tl_replace_all:Nnn \l_tmpa_tl { ~ } { \c_space_tl }
\tl_map_inline:Nn \l_tmpa_tl
{
\tl_if_blank:eTF { ##1 }
{ ~ } % don't advance the counter and issue a space
{
\textcolor{ \int_if_odd:nTF { \l_tmpa_int } { purple } { candy } } { ##1 }
\int_incr:N \l_tmpa_int
}
}
}
\prg_generate_conditional_variant:Nnn \tl_if_blank:n { e } { T,F,TF,p }
\ExplSyntaxOff
\begin{document}
\markletters{Hello World}
\end{document}
答案3
您要求进行例行检查宏参数包括一个单倍空间尽管\afterassignment-\let
您的示例中的 -loop不是按宏参数进行迭代,而是按标记进行迭代。除此之外,使用该循环,您无法让 (La)TeX “查看”任何宏参数的标记,但可以让 (La)TeX “查看”控制字标记 的含义\xchar
。您无法准确推断出从中得到其含义的标记类型\xchar
:这确实可能是一个显式空格字符标记。但这也可能是一个隐式空格标记,即某个等于\let
显式空格字符标记的控制序列,例如\@sptoken
。
因此,下面的答案/例子很可能对您没有任何用处。
尽管如此,它可能对那些偶然发现你的问题但确实需要一个用于检测宏参数中的(显式)空间标记的例程的人有用。
下面的示例提供了两个例程:
该宏\UD@CheckWhetherLeadingSpace
可用于查明宏参数的第一个标记是否是显式空格字符标记(字符代码 32,类别代码 10)。
其要点是\UD@CheckWhetherLeadingSpace
:附加一个空格标记(以确保至少有一个),然后收集直到第一个空格标记的所有内容,然后查看是否收集了“空”。使用一些括号破解来删除余数。
该宏\UD@CheckWhetherSingleSpace
可用于查明宏参数是否由单个显式空格字符标记(字符代码 32,类别代码 10)组成。
的要点\UD@CheckWhetherSingleSpace
是:应用\UD@CheckWhetherLeadingSpace
。 如果有前导空格,请检查将其删除后是否为空。
这些宏仅适用于显式空格字符标记(字符代码 32,类别代码 10)。它们既不适用于隐式空格字符,也不适用于字符代码为 32 但类别代码不同于 10 的字符标记,也不适用于所谓的“有趣空格”(字符代码不同于 32,类别代码为 10 - 如果我没记错的话,“有趣空格”只能通过更改空格字符的或然后将\lccode
相应的应用于空格字符(然后可能申请从显式有趣空格获得隐式有趣空格)...)。 \uccode
\lowercase
\uppercase
\let
这些宏不需要 e-TeX 扩展,并且也适用于完全扩展上下文等。
由于-expansion,您在触发两个扩展步骤后/在被/ “命中”两个 后\romannumeral
获得结果。\UD@CheckWhetherLeadingSpace
\UD@CheckWhetherSingleSpace
\expandafter
(顺便一提:
请注意,(La)TeX 在收集未分隔的宏参数时会跳过未嵌套在括号中的显式(无意义的)空格标记。收集分隔的宏参数时不会跳过这些空格标记。
例如,使用\def\threeargs#1#2#3{#1#2#3}
和\threeargs a b c
您将得到#1
= a
,#2
= b
,#3
=,尽管和之间以及和之间c
会有明确的空格字符标记。a
b
b
c
我认为这就是为什么你问题中的例子中的循环不是按宏参数进行迭代而是按标记进行迭代的原因之一。)
\documentclass[a4paper]{article}
\makeatletter
%%----------------------------------------------------------------------
\newcommand\UD@firstoftwo[2]{#1}%
\newcommand\UD@secondoftwo[2]{#2}%
\newcommand\UD@exchange[2]{#2#1}%
\newcommand\UD@gobblespace{}%
\UD@firstoftwo{\def\UD@gobblespace}{} {}%
%%----------------------------------------------------------------------
%% Check whether argument is empty:
%%......................................................................
%% \UD@CheckWhetherNull{<Argument which is to be checked>}%
%% {<Tokens to be delivered in case that argument
%% which is to be checked is empty>}%
%% {<Tokens to be delivered in case that argument
%% which is to be checked is not empty>}%
%% The gist of this macro comes from Robert R. Schneck's \ifempty-macro:
%% <https://groups.google.com/forum/#!original/comp.text.tex/kuOEIQIrElc/lUg37FmhA74J>
\newcommand\UD@CheckWhetherNull[1]{%
\romannumeral0\expandafter\UD@secondoftwo\string{\expandafter
\UD@secondoftwo\expandafter{\expandafter{\string#1}\expandafter
\UD@secondoftwo\string}\expandafter\UD@firstoftwo\expandafter{\expandafter
\UD@secondoftwo\string}\expandafter\expandafter\UD@firstoftwo{ }{}%
\UD@secondoftwo}{\expandafter\expandafter\UD@firstoftwo{ }{}\UD@firstoftwo}%
}%
%%-----------------------------------------------------------------------------
%% Check whether brace-balanced argument starts with a space-token
%%.............................................................................
%% \UD@CheckWhetherLeadingSpace{<Argument which is to be checked>}%
%% {<Tokens to be delivered in case <argument
%% which is to be checked>'s 1st token is a
%% space-token>}%
%% {<Tokens to be delivered in case <argument
%% which is to be checked>'s 1st token is not
%% a space-token>}%
\newcommand\UD@CheckWhetherLeadingSpace[1]{%
\romannumeral0\UD@CheckWhetherNull{#1}%
{\expandafter\expandafter\UD@firstoftwo{ }{}\UD@secondoftwo}%
{\expandafter\UD@secondoftwo\string{\UD@CheckWhetherLeadingSpaceB.#1 }{}}%
}%
\newcommand\UD@CheckWhetherLeadingSpaceB{}%
\long\def\UD@CheckWhetherLeadingSpaceB#1 {%
\expandafter\UD@CheckWhetherNull\expandafter{\UD@secondoftwo#1{}}%
{\UD@exchange{\UD@firstoftwo}}{\UD@exchange{\UD@secondoftwo}}%
{\UD@exchange{ }{\expandafter\expandafter\expandafter\expandafter
\expandafter\expandafter\expandafter}\expandafter\expandafter
\expandafter}\expandafter\UD@secondoftwo\expandafter{\string}%
}%
%%-----------------------------------------------------------------------------
%% Check whether brace-balanced argument consists only of a single space-token
%%.............................................................................
\newcommand\UD@CheckWhetherSingleSpace[1]{%
\romannumeral0\UD@CheckWhetherLeadingSpace{#1}{%
\expandafter\UD@CheckWhetherNull\expandafter{\UD@gobblespace#1}%
{\UD@exchange{\UD@firstoftwo}}{\UD@exchange{\UD@secondoftwo}}%
}{\UD@exchange{\UD@secondoftwo}}{\UD@exchange{ }{\expandafter}}%
}%
%\makeatother
\begin{document}
\UD@CheckWhetherLeadingSpace{ text}{Leading explicit space token}{No leading explicit space token}
\UD@CheckWhetherLeadingSpace{ }{Leading explicit space token}{No leading explicit space token}
\UD@CheckWhetherLeadingSpace{text}{Leading explicit space token}{No leading explicit space token}
% empty argument:
\UD@CheckWhetherLeadingSpace{}{Leading explicit space token}{No leading explicit space token}
% two space tokens:
\expandafter\UD@CheckWhetherLeadingSpace\expandafter{\@firstofone{ } }%
{Leading explicit space token}{No leading explicit space token}
\noindent\hrulefill
\UD@CheckWhetherSingleSpace{ text}{Single explicit space token}{Not a single explicit space token}
\UD@CheckWhetherSingleSpace{ }{Single explicit space token}{Not a single explicit space token}
\UD@CheckWhetherSingleSpace{text}{Single explicit space token}{Not a single explicit space token}
% empty argument:
\UD@CheckWhetherSingleSpace{}{Single explicit space token}{Not a single explicit space token}
% two space tokens:
\expandafter\UD@CheckWhetherSingleSpace\expandafter{\@firstofone{ } }%
{Single explicit space token}{Not a single explicit space token}
\end{document}