所以这里是一种获取给定字符串的第一个、第二个和剩余元素(字符)的方法:
\makeatletter
\newcommand{\firstof}[1]{\@car#1\@nil}
\newcommand{\secondof}[1]{\expandafter\@car\@cdr#1\@nil\@nil}
\newcommand{\restof}[1]{\expandafter\@cdr\@cdr#1\@nil\@nil}
\makeatother
但无论我怎么努力,我还是找不到办法第三元素——下面的 MWE 显示了明显的第一次尝试……现在,我明白这与 TeX 扩展其宏的复杂方式有关,但我将不胜感激任何帮助,因为我被卡住了!(在第一次尝试之后,我又尝试了几件事(一些受到启发这),但没有任何效果。)提前谢谢您!
\documentclass[a4paper,12pt,dvipsnames*]{article}
\makeatletter
\newcommand{\firstof}[1]{\@car#1\@nil}
\newcommand{\secondof}[1]{\expandafter\@car\@cdr#1\@nil\@nil}
\newcommand{\restof}[1]{\expandafter\@cdr\@cdr#1\@nil\@nil}
% Does not work!
\newcommand{\thirdof}[1]{\expandafter\@cdr\@cdr\@cdr#1\@nil\@nil\@nil}
\makeatother
\begin{document}
\section*{Some topic}
\begin{itemize}
\item \verb+\firstof{ABCD}+: $\firstof{ABCD}$
\item \verb+\secondof{ABCD}+: $\secondof{ABCD}$
\item \verb+\restof{ABCD}+: $\restof{ABCD}$
% \item \verb+\thirdof{ABCD}+: $\thirdof{ABCD}$ % DOES NOT WORK
\end{itemize}
\end{document}
答案1
使用一堆\expandafter
令牌可能很有趣,但是现在有比\@car
和更好的方法\@cdr
。
\documentclass{article}
\ExplSyntaxOn
\NewExpandableDocumentCommand{\firstof}{m}
{
%\tl_item:nn { #1 } { 1 }
\tl_head:n { #1 }
}
\NewExpandableDocumentCommand{\secondof}{m}
{
\tl_item:nn { #1 } { 2 }
}
\NewExpandableDocumentCommand{\thirdof}{m}
{
\tl_item:nn { #1 } { 3 }
}
\NewExpandableDocumentCommand{\nthof}{mm}
{
\tl_item:nn { #2 } { #1 }
}
\NewExpandableDocumentCommand{\restof}{O{2}m}
{
\tl_range:nnn { #2 } { #1 } { -1 }
}
\ExplSyntaxOff
\begin{document}
\section*{Some topic}
\begin{itemize}
\item \verb+\firstof{ABCD}+: \firstof{ABCD}
\item \verb+\secondof{ABCD}+: \secondof{ABCD}
\item \verb+\thirdof{ABCD}+: \thirdof{ABCD}
\item \verb+\nthof{4}{ABCD}+: \nthof{4}{ABCD}
\item \verb+\restof{ABCD}+: \restof{ABCD}
\item \verb+\restof[3]{ABCD}+: \restof[3]{ABCD}
\end{itemize}
\end{document}
答案2
模仿的定义\@car
但占据第三个位置(此定义不检查列表是否足够长)
\documentclass[a4paper,12pt,dvipsnames*]{article}
\makeatletter
\newcommand{\firstof}[1]{\@car#1\@nil}
\newcommand{\secondof}[1]{\expandafter\@car\@cdr#1\@nil\@nil}
\newcommand{\thirdof}[1]{\@thirdof#1\@nil}
\def\@thirdof#1#2#3#4\@nil{#3}
\newcommand{\restof}[1]{\@restof#1\@nil}
\def\@restof#1#2#3#4\@nil{#4}
% Does not work!
\makeatother
\begin{document}
\section*{Some topic}
\begin{itemize}
\item \verb+\firstof{ABCD}+: $\firstof{ABCD}$
\item \verb+\secondof{ABCD}+: $\secondof{ABCD}$
\item \verb+\thirdof{ABCD}+: $\thirdof{ABCD}$ % DOES NOT WORK
\item \verb+\restof{ABCD}+: $\restof{ABCD}$
\end{itemize}
\end{document}
答案3
如果这背后有一个实际的用例场景,而不仅仅是学术兴趣:
TeX 中“字符串”的概念比较模糊。
字符串可能包含哪些类型的标记?
您希望如何处理空格和花括号?
字符串可能包含哪些类型的字符?.tex 输入文件是否以 utf-8 编码,并且字符串可能包含多字节字符?如果是这样,则需要特别考虑,因为对于 8 位 TeX 引擎,多字节字符可能产生与在 .tex 输入文件中对其进行编码所需的字节数一样多的标记,这反过来意味着对于 8 位引擎,这些字符不能作为由未用花括号括起来的单个标记组成的无分隔符参数进行处理。
为了找乐子——来一场\expandafter
群交怎么样?
\documentclass[a4paper,12pt]{article}
\makeatletter
\newcommand{\firstof}[1]{\@car#1\@nil}%
\newcommand{\secondof}[1]{\expandafter\@car\@cdr#1\@nil\@nil}%
\newcommand{\thirdof}[1]{\expandafter\expandafter\expandafter\@car\expandafter\@cdr\@cdr#1\@nil\@nil\@nil}%
% As \@cdr grabs an undelimited argument and a delimited argument, the following, where \@cdr
% is used three times, breaks in case #1 does not contain at least three components that can
% be grabbed as undelimited argument:
\newcommand{\restof}[1]{\expandafter\expandafter\expandafter\@cdr\expandafter\@cdr\@cdr#1\@nil\@nil\@nil}
%----------------------------------------------------------------------------------------------
%
% The following might be of academical interest in case you don't mind
% - inscrutable behavior in case of the string containing curly braces of category 1/2
% - explicit space tokens being removed and not being counted when detecting/
% delivering the K-th element of the string while spaces not being removed
% from the rest of the string:
%
\@ifdefinable\@stopromannumeral{\chardef\@stopromannumeral=`\^^00}%
\newcommand\PassFirstToSecond[2]{#2{#1}}%
\newcommand\KthOf[3]{%
% #1 - number K
% #2 - tokens in case there are no K undelimited arguments.
% #3 - string
\romannumeral
\expandafter\@KthOfCheck\expandafter{\number\numexpr(#1)\relax}{#2}{#3}{\@car}%
}%
\newcommand\RestBehindKthOf[3]{%
% #1 - number K
% #2 - tokens in case there are no K undelimited arguments.
% #3 - string
\romannumeral
\expandafter\@KthOfCheck\expandafter{\number\numexpr(#1)\relax}{#2}{#3}{\@cdr}%
}%
\newcommand\@KthOfCheck[4]{%
% #1 - number K
% #2 - tokens in case there are no K undelimited arguments.
% #3 - string
% #4 = token to prepend when thingies are removed, either \@car or \@cdr
\ifnum0<#1 \expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi
{%
\expandafter\expandafter\expandafter\PassFirstToSecond
\expandafter\expandafter\expandafter{\expandafter\@cdr
\romannumeral\numexpr(#1)*(1000)\relax\@nil}{\@KthOfLoop}{#4}%
{#3}{#2}%
}{%
\ifnum0=#1 \expandafter\@firstofone\else\expandafter\@gobble\fi
{%
\ifx\@car#4\else\expandafter\@firstoftwo\expandafter\@firstoftwo\fi
}\@secondoftwo
{\@stopromannumeral#3}{\@stopromannumeral#2}%
}%
}%
\newcommand\@KthOfLoop[4]{%
% #1 = as many m as characters are still to be removed from string #3 before doing #2
% #2 = token to prepend when thingies are removed, either \@car or \@cdr
% #3 = remaining String
% #4 = tokens in case there are no K undelimited arguments.
\ifcat$\detokenize\expandafter{\@secondoftwo#3{}{}}$%
\expandafter\@secondoftwo\else\expandafter\@firstoftwo\fi
{%
\ifx\@nil#1\@nil\expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi
{\expandafter\@firstofone\expandafter{\expandafter\@stopromannumeral#2#3\@nil}}%
{%
\expandafter\PassFirstToSecond\expandafter{\@cdr#3\@nil}%
{\expandafter\@KthOfLoop\expandafter{\@cdr#1\@nil}{#2}}{#4}%
}%
}%
{\@stopromannumeral#4}%
}%
%----------------------------------------------------------------------------------------------
\makeatother
\begin{document}
\ttfamily\selectfont\frenchspacing
\noindent
\verb+|\firstof{ABCD}|+: |\firstof{ABCD}|\\
\verb+|\secondof{ABCD}|+: |\secondof{ABCD}|\\
\verb+|\thirdof{ABCD}|+: |\thirdof{ABCD}|\\ % DOES WORK
\verb+|\restof{ABCD}|+: |\restof{ABCD}|\\
\verb+|\KthOf{-1}{There is no minusoneth.}{ABCD}|+: |\KthOf{-1}{There is no minusoneth.}{ABCD}|\\
\verb+|\KthOf{0}{There is no zeroth.}{ABCD}|+: |\KthOf{0}{There is no zeroth.}{ABCD}|\\
\verb+|\KthOf{1}{There is no first.}{ABCD}|+: |\KthOf{1}{There is no first.}{ABCD}|\\
\verb+|\KthOf{2}{There is no second.}{ABCD}|+: |\KthOf{2}{There is no second.}{ABCD}|\\
\verb+|\KthOf{3}{There is no third.}{ABCD}|+: |\KthOf{3}{There is no third.}{ABCD}|\\
\verb*+|\KthOf{3}{There is no third.}{ A B C D }|+: |\KthOf{3}{There is no third.}{ A B C D }|\\
\verb+|\KthOf{4}{There is no fourth.}{ABCD}|+: |\KthOf{4}{There is no fourth.}{ABCD}|\\
\verb+|\KthOf{5}{There is no fifth.}{ABCD}|+: |\KthOf{5}{There is no fifth.}{ABCD}|\\
\verb+|\RestBehindKthOf{-1}{There is no minusoneth.}{ABCD}|+: |\RestBehindKthOf{-1}{There is no minusoneth.}{ABCD}|\\
\verb+|\RestBehindKthOf{0}{There is no zeroth.}{ABCD}|+: |\RestBehindKthOf{0}{There is no zeroth.}{ABCD}|\\
\verb+|\RestBehindKthOf{1}{There is no first.}{ABCD}|+: |\RestBehindKthOf{1}{There is no first.}{ABCD}|\\
\verb+|\RestBehindKthOf{2}{There is no second.}{ABCD}|+: |\RestBehindKthOf{2}{There is no second.}{ABCD}|\\
\verb+|\RestBehindKthOf{3}{There is no third.}{ABCD}|+: |\RestBehindKthOf{3}{There is no third.}{ABCD}|\\
\verb*+|\RestBehindKthOf{3}{There is no third.}{ A B C D }|+: |\RestBehindKthOf{3}{There is no third.}{ A B C D }|\\
\verb+|\RestBehindKthOf{4}{There is no fourth.}{ABCD}|+: |\RestBehindKthOf{4}{There is no fourth.}{ABCD}|\\
\verb+|\RestBehindKthOf{5}{There is no fifth.}{ABCD}|+: |\RestBehindKthOf{5}{There is no fifth.}{ABCD}|
\end{document}
上述代码只是为了满足学术界对宏编程的兴趣。
如果 .tex-input 文件采用多字节编码 utf-8 进行编码并且正在使用 8 位引擎,则 .tex-input 文件中由多个字节组成的 utf-8 字符可能会产生多个标记,因此您无法将 .tex-input 中的字符处理为无分隔符参数,这些字符由未括在花括号中的单个标记组成。