我编写了以下代码来验证宏的参数是否是一个或多个字段的列表:
\documentclass{amsart}
\scrollmode
\usepackage{xcolor}
\usepackage{xr-hyper}
\usepackage[unicode]{hyperref}
\hypersetup{colorlinks}
\usepackage{framed}
\usepackage{trace}
%\traceon
%\traceoff
\def\OneTheorem{the theorem }
\def\ManyTheorems{theorems }
\def\LastRef{LastRef}%
\def\PrintTheorem#1{\PrtTheorem#1||LastRef||}%
\def\PrtTheorem#1||#2||{\def\temp{#2}%
\ifx\temp\LastRef%
\OneTheorem
\else%
\ManyTheorems
\fi%
}
\def\Refs#1{\RefList#1||LastRef||}%
\def\TheoremSep{}
\def\RefList#1||{\def\temp{#1}%
\ifx\temp\LastRef%
\else%
\TheoremSep\DoTheoremRef{#1}
\def\TheoremSep{, }
\expandafter\RefList%
\fi%
}
\def\DoTheoremRef#1{\DoRefs#1|:LastTRef|:}%
\def\LastTRef{LastTRef}%
\def\DoRefs#1|:#2|:{\def\tempA{#2}%
\ifx\tempA\LastTRef%
(#1)
\else%
(#1-#2)
\fi%
}
\newenvironment{ProofRefA}[1]{%
{\sc ProofA of \PrintTheorem{#1}
\Refs{#1}
}
}%
{\qed}
\begin{document}
\title{Test File}
\begin{abstract}
\end{abstract}
\maketitle
\begin{ProofRefA}{ab1||ab2|:ab3||ab4||ab5}
\end{ProofRefA}
\begin{ProofRefA}{ab5}
\end{ProofRefA}
\end{document}
该宏\PrintTheorem
按我预期的方式工作。但是,它会产生额外的输出。可以选择添加以下行:
\expandafter\RefList%
但是,它将是宏的递归调用\RefList
。我将看到几个单词定理的副本,并且宏的输出\Refs
将消失。经过一番搜索,我找到了代码:
\documentclass{amsart}
\scrollmode
\usepackage{xcolor}
\usepackage{xr-hyper}
\usepackage[unicode]{hyperref}
\hypersetup{colorlinks}
\usepackage{framed}
\usepackage{trace}
%\traceon
%\traceoff
\def\OneTheorem{the theorem }
\def\ManyTheorems{theorems }
\def\LastRef{LastRef}%
\def\PrintTheorem#1{\PrtTheorem#1||LastRef||}%
\def\PrtTheorem#1||#2||{\def\temp{#2}%
\ifx\temp\LastRef%
\OneTheorem
\else%
\ManyTheorems
\expandafter\PrtTheorem
\fi%
}
\def\DoNothing#1||
{
\ifx\temp\LastRef%
\else%
\expandafter\DoNothing
\fi%
}
\def\Refs#1{\RefList#1||LastRef||}%
\def\TheoremSep{}
\def\RefList#1||{\def\temp{#1}%
\ifx\temp\LastRef%
\else%
\TheoremSep\DoTheoremRef{#1}
\def\TheoremSep{, }
\expandafter\RefList%
\fi%
}
\def\DoTheoremRef#1{\DoRefs#1|:LastTRef|:}%
\def\LastTRef{LastTRef}%
\def\DoRefs#1|:#2|:{\def\tempA{#2}%
\ifx\tempA\LastTRef%
(#1)
\else%
(#1-#2)
\fi%
}
\newenvironment{ProofRefA}[1]{%
{\sc ProofA of \PrintTheorem{#1}
\Refs{#1}
}
}%
{\qed}
\begin{document}
\title{Test File}
\begin{abstract}
\end{abstract}
\maketitle
\begin{ProofRefA}{ab1||ab2|:ab3||ab4||ab5}
\end{ProofRefA}
\begin{ProofRefA}{ab5}
\end{ProofRefA}
\end{document}
它运行得更好,但我仍然没有看到宏的输出\Refs
。
答案1
我不会用它expl3
做这份工作。
\documentclass{amsart}
\def\listTheorems#1{\bgroup\ltheorA#1||||}
\def\ltheorA#1||{\ifx^#1^\egroup
\else \ltheorS({\ltheorB#1::})\expandafter\ltheorA\fi}
\def\ltheorB#1:{\ifx^#1^\else\ltheorD#1\expandafter\ltheorB\fi}
\def\ltheorS{\def\ltheorS{, }}
\def\ltheorD{\def\ltheorD{--}}
\newenvironment{ProofRefA}[1]{%
{\sc ProofA of {\rm \listTheorems{#1}}
}
}%
{\qed}
\begin{document}
\begin{ProofRefA}{ab1||ab2:ab3||ab4||ab5}
Text text.
\end{ProofRefA}
\begin{ProofRefA}{ab5}
Text text.
\end{ProofRefA}
\end{document}
答案2
我会用它expl3
来做这项工作。
\documentclass{amsart}
\ExplSyntaxOn
\NewDocumentEnvironment{ProofRefA}{m}
{
\textsc{ProofA~of}~\kleyn_proofref:n { #1 }
}
{\qed}
\seq_new:N \l__kleyn_proofref_in_seq
\seq_new:N \l__kleyn_proofref_out_seq
\tl_new:N \l__kleyn_proofref_range_tl
\cs_new_protected:Nn \kleyn_proofref:n
{
\seq_set_split:Nnn \l__kleyn_proofref_in_seq { || } { #1 }
\seq_set_map:NNn \l__kleyn_proofref_out_seq \l__kleyn_proofref_in_seq
{
(\str_if_in:nnTF {##1} {:} { \__kleyn_proofref_range:n { ##1 } } { ##1 })
}
\seq_use:Nn \l__kleyn_proofref_out_seq { ,~ }
}
\cs_new_protected:Nn \__kleyn_proofref_range:n
{
\tl_set:Nn \l__kleyn_proofref_range_tl { #1 }
\tl_replace_once:NVn \l__kleyn_proofref_range_tl \c_colon_str { -- }
\tl_use:N \l__kleyn_proofref_range_tl
}
\cs_generate_variant:Nn \tl_replace_once:Nnn { NV }
\ExplSyntaxOff
\begin{document}
\title{Test File}
\begin{abstract}
\end{abstract}
\maketitle
\begin{ProofRefA}{ab1||ab2:ab3||ab4||ab5}
\end{ProofRefA}
\begin{ProofRefA}{ab5}
\end{ProofRefA}
\end{document}
这个想法是将输入拆分为||
,然后在项目周围添加括号,但检查冒号是否在项目内,如果在项目内,则用短划线代替冒号。最后,经过处理的项目以“逗号和空格”分隔输出。
答案3
如果您有宏参数是否为空的测试,那么您可以通过将该分隔符附加到该参数,然后从参数中删除该分隔符第一次出现之前的所有内容,来测试该参数是否包含特定分隔符(未嵌套在括号中)。如果结果为空,则该参数不包含该分隔符。如果不为空,则该参数至少包含一个该分隔符的实例。
例如,空测试可以是:
\newcommand\UD@firstoftwo[2]{#1}%
\newcommand\UD@secondoftwo[2]{#2}%
\@ifdefinable\UD@stopromannumeral{\chardef\UD@stopromannumeral=`\^^00}%
\newcommand\UD@CheckWhetherNull[1]{%
\romannumeral\expandafter\UD@secondoftwo\string{\expandafter
\UD@secondoftwo\expandafter{\expandafter{\string#1}\expandafter
\UD@secondoftwo\string}\expandafter\UD@firstoftwo\expandafter{\expandafter
\UD@secondoftwo\string}\expandafter\UD@stopromannumeral\UD@secondoftwo}{%
\expandafter\UD@stopromannumeral\UD@firstoftwo}%
}%
或者
\newcommand\UD@firstoftwo[2]{#1}%
\newcommand\UD@secondoftwo[2]{#2}%
\@ifdefinable\UD@stopromannumeral{\chardef\UD@stopromannumeral=`\^^00}%
\newcommand\UD@CheckWhetherNull[1]{%
\romannumeral\ifcat$\detokenize{#1}$%
\expandafter\expandafter\expandafter\UD@stopromannumeral\expandafter\UD@firstoftwo
\else
\expandafter\expandafter\expandafter\UD@stopromannumeral\expandafter\UD@secondoftwo
\fi
}%
如果你想检查宏参数是否不包含|:
(除非嵌套在花括号之间),你可以定义
\newcommand\CheckWhetherNoBarColon[1]{%
\expandafter\UD@CheckWhetherNull\expandafter{\UD@gobbletobarcolon#1|:}%
}%
\@ifdefinable\UD@gobbletobarcolon{\long\def\UD@gobbletobarcolon#1|:{}}%
并按如下方式使用:
\CheckWhetherNoBarColon{⟨argument to check⟩}%
{⟨tokens in case ⟨argument to check⟩ does not contain |: (unless nested between curly braces)⟩}%
{⟨tokens in case ⟨argument to check⟩ does contain |: which is not nested between curly braces⟩}%
在您的场景中,嵌套尾递归(在||
-delimited 项列表上进行迭代,依次将每个项作为|:
-delimited 参数列表)可能会起到作用:
\makeatletter
%%=============================================================================
%% Paraphernalia:
%% \UD@firstoftwo, \UD@secondoftwo, \UD@Exchange, \UD@PassFirstToSecond,
%% \UD@stopromannumeral, \UD@CheckWhetherNull, \UD@CheckWhetherBlank,
%%=============================================================================
\newcommand\UD@firstoftwo[2]{#1}%
\newcommand\UD@secondoftwo[2]{#2}%
\newcommand\UD@Exchange[2]{#2#1}%
\newcommand\UD@PassFirstToSecond[2]{#2{#1}}%
\@ifdefinable\UD@stopromannumeral{\chardef\UD@stopromannumeral=`\^^00}%
%%-----------------------------------------------------------------------------
%% 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]{%
\romannumeral\expandafter\UD@secondoftwo\string{\expandafter
\UD@secondoftwo\expandafter{\expandafter{\string#1}\expandafter
\UD@secondoftwo\string}\expandafter\UD@firstoftwo\expandafter{\expandafter
\UD@secondoftwo\string}\expandafter\UD@stopromannumeral\UD@secondoftwo}{%
\expandafter\UD@stopromannumeral\UD@firstoftwo}%
}%
%%-----------------------------------------------------------------------------
%% Check whether argument is blank (empty or only spaces):
%%-----------------------------------------------------------------------------
%% -- Take advantage of the fact that TeX discards space tokens when
%% "fetching" _un_delimited arguments: --
%% \UD@CheckWhetherBlank{<Argument which is to be checked>}%
%% {<Tokens to be delivered in case that
%% argument which is to be checked is blank>}%
%% {<Tokens to be delivered in case that argument
%% which is to be checked is not blank>}%
\newcommand\UD@CheckWhetherBlank[1]{%
\romannumeral\expandafter\expandafter\expandafter\UD@secondoftwo
\expandafter\UD@CheckWhetherNull\expandafter{\UD@firstoftwo#1{}{}}%
}%
%%-----------------------------------------------------------------------------
%% Trim one leading and one trailing space if present.
%% This is borrrowed from trimspaces.sty v 1.1 2009/09/17 by
%% Will Robertson/Morten Høgholm.
\begingroup
\catcode`\Q=3
\UD@firstoftwo{%
\endgroup
\newcommand\UDtrim@spaces[1]{\romannumeral-`\q\UDtrim@trim@\noexpand#1Q Q}%
\@ifdefinable\UDtrim@trim@{\long\def\UDtrim@trim@#1 Q{\UDtrim@trim@@#1Q}}%
\@ifdefinable\UDtrim@trim@@{\long\def\UDtrim@trim@@#1Q#2{#1}}%
}{}%
%%-----------------------------------------------------------------------------
\@ifdefinable\UD@gobbletoBarBar{\long\def\UD@gobbletoBarBar#1||{}}%
\@ifdefinable\UD@keeptoBarBar{\long\def\UD@keeptoBarBar#1||{#1}}%
\@ifdefinable\UD@gobbletoBarColon{\long\def\UD@gobbletoBarColon#1|:{}}%
\@ifdefinable\UD@keeptoBarColon{\long\def\UD@keeptoBarColon#1|:{#1}}%
\begingroup
\def\importfrozenrelax#1{%
\endgroup
\@ifdefinable\UD@RemoveFromBarBarTillFrozenrelax{%
\long\def\UD@RemoveFromBarBarTillFrozenrelax##1||##2#1{{##1}||}%
}%
\@ifdefinable\UD@RemoveFromBarColonTillFrozenrelax{%
\long\def\UD@RemoveFromBarColonTillFrozenrelax##1|:##2#1{{##1}|:}%
}%
%% \UD@ExtractFirstDelimitedArg{<gobble-macro>}%
%% {<keep-macro>}%
%% {<remove-till-frozen-relax-mmacro>}%
%% {<delimiter>}%
%% {<list of delimited arguments>}%
\newcommand\UD@ExtractFirstDelimitedArg[5]{%
\romannumeral\UD@ExtractFirstDelimitedArgLoop{{{}}##5##4#1}{##1}{##2}{##3}%
}%
}%
\expandafter\expandafter\expandafter\importfrozenrelax
\expandafter\expandafter\expandafter{\expandafter\expandafter\ifnum0=0\fi}%
%%-----------------------------------------------------------------------------
\newcommand\UD@ExtractFirstDelimitedArgLoop[4]{%
\expandafter\UD@CheckWhetherNull\expandafter{#2#1}%
{%
\expandafter\UD@stopromannumeral
\expandafter{%
\romannumeral\expandafter\UD@firstoftwo\expandafter\UD@stopromannumeral#3#1%
}%
}%
{\expandafter\UD@ExtractFirstDelimitedArgLoop\expandafter{#4#1}{#2}{#3}{#4}}%
}%
%%-----------------------------------------------------------------------------
% \UD@barbardelimitedloop{<list gathered so far>}% initialize empty
% {<stuff to prepend if loop terminates this time>}% initialize to phrase in case no references are specified
% {<separator to prepend in this iteration>}% initialize empty
% {<separator to prepend in next iteration>}% initialize to comma
% {<stuff to prepend if loop terminates with one element>}% initialize to phrase in case a single reference is specified
% {<stuff to prepend if loop terminates with several elements>}% initialize to phrase in case deveral references are specified
% {<remaining list>}% initialize to environment's argument
\newcommand\UD@barbardelimitedloop[7]{%
\UD@CheckWhetherBlank{#7}{#2#1}{%
\expandafter\UD@CheckWhetherNull\expandafter{\UD@gobbletoBarBar#7||}{%
\UD@barbardelimitedprepend{#7}{#1}{#2}{#3}{#4}{#5}{#6}{}%
}{%
\expandafter\UD@PassFirstToSecond\expandafter{\UD@gobbletoBarBar#7}{%
\expandafter\expandafter\expandafter\UD@barbardelimitedprepend
\UD@ExtractFirstDelimitedArg{\UD@gobbletoBarBar}{\UD@keeptoBarBar}{\UD@RemoveFromBarBarTillFrozenrelax}{||}{#7}%
{#1}{#2}{#3}{#4}{#5}{#6}%
}%
}%
}%
}%
\newcommand\UD@barbardelimitedprepend[8]{%
% #1 - <item in this iteration>
% #2 - <list gathered so far>
% #3 - <stuff to prepend if loop terminates this time>
% #4 - <separator to prepend in this iteration>
% #5 - <separator to prepend in next iteration>
% #6 - <stuff to prepend if loop terminates with one element>
% #7 - <stuff to prepend if loop terminates with several elements>
% #8 - <remaining list>
\UD@CheckWhetherBlank{#1}{%
\UD@barbardelimitedloop{#2}{#3}{#4}{#5}{#6}%
}{%
\UD@barcolondelimitedloop{}%
{\UD@nojoinwithbarbardelimitedloop{#2}{#3}{#4}{#5}{#6}}%
{}%
{--}%
{\UD@joinwithbarbardelimitedloop{#2#4}{#6}{#5}{#5}{#7}}%
{\UD@joinwithbarbardelimitedloop{#2#4}{#7}{#5}{#5}{#7}}%
{#1}%
}%
{#7}{#8}%
}%
\newcommand\UD@joinwithbarbardelimitedloop[6]{\UD@barbardelimitedloop{#1(#6)}{#2}{#3}{#4}{#5}}%
\newcommand\UD@nojoinwithbarbardelimitedloop[6]{\UD@barbardelimitedloop{#1}{#2}{#3}{#4}{#5}}%
%%-----------------------------------------------------------------------------
% \UD@barcolondelimitedloop{<list gathered so far>}% initialize empty
% {<stuff to prepend if loop terminates this time>}%
% {<separator to prepend in this iteration>}% initialize empty
% {<separator to prepend in next iteration>}% initialiize to --
% {<stuff to prepend if loop terminates with one element>}%
% {<stuff to prepend if loop terminates with several elements>}%
% {<remaining list>}% initialize to current item of ||-list
\newcommand\UD@barcolondelimitedloop[7]{%
\UD@CheckWhetherBlank{#7}{#2{#1}}{%
\expandafter\UD@CheckWhetherNull\expandafter{\UD@gobbletoBarColon#7|:}{%
\UD@barcolondelimitedprepend{#7}{#1}{#2}{#3}{#4}{#5}{#6}{}%
}{%
\expandafter\UD@PassFirstToSecond\expandafter{\UD@gobbletoBarColon#7}{%
\expandafter\expandafter\expandafter\UD@barcolondelimitedprepend
\UD@ExtractFirstDelimitedArg{\UD@gobbletoBarColon}{\UD@keeptoBarColon}{\UD@RemoveFromBarColonTillFrozenrelax}{|:}{#7}%
{#1}{#2}{#3}{#4}{#5}{#6}%
}%
}%
}%
}%
\newcommand\UD@barcolondelimitedprepend[8]{%
\UD@CheckWhetherBlank{#1}{%
\UD@barcolondelimitedloop{#2}{#3}{#4}{#5}{#6}{#7}{#8}%
}{%
\expandafter\UD@barcolondelimitedloop\expandafter{%
\romannumeral
\expandafter\expandafter\expandafter\UD@Exchange
\expandafter\expandafter\expandafter{\UDtrim@spaces{#1}}{\UD@stopromannumeral#2#4}%
}{#6}{#5}{#5}{#7}{#7}{#8}%
}%
}%
%%=============================================================================
\newcommand\PrintTheorem[1]{%
\UD@barbardelimitedloop{}%
{Proof(s) of \UnspecifiedTheorems}%
{}%
{, }%
{Proof of \OneTheorem}%
{Proofs of \ManyTheorems}%
{#1}%
}%
\makeatother
\documentclass{amsart}
\newcommand*\UnspecifiedTheorems{some unspecified theorem(s) }
\newcommand*\OneTheorem{the theorem }
\newcommand*\ManyTheorems{theorems }
\newenvironment{ProofRefA}[1]{{\sc ProofA/\PrintTheorem{#1}}}{\qed}
\begin{document}
\noindent\verb+\begin{ProofRefA}{ab1||ab2|:ab3||ab4||ab5}\end{ProofRefA}+:\\
\begin{ProofRefA}{ab1||ab2|:ab3||ab4||ab5}\end{ProofRefA}
\par\kern\dp\strutbox\hrule\kern\dp\strutbox
\noindent\verb+\begin{ProofRefA}{ab5}\end{ProofRefA}+:\\
\begin{ProofRefA}{ab5}\end{ProofRefA}
\par\kern\dp\strutbox\hrule\kern\dp\strutbox
\noindent\verb+\begin{ProofRefA}{ ab1 || ab2|:ab3 || ab4 || ab5 }\end{ProofRefA}+:\\
\begin{ProofRefA}{ ab1 || ab2|:ab3 || ab4 || ab5 }\end{ProofRefA}
\par\kern\dp\strutbox\hrule\kern\dp\strutbox
\noindent\verb+\begin{ProofRefA}{ ab2|: || }\end{ProofRefA}+:\\
\begin{ProofRefA}{ ab2|: || }\end{ProofRefA}
\par\kern\dp\strutbox\hrule\kern\dp\strutbox
\noindent\verb+\begin{ProofRefA}{ ab2|: }\end{ProofRefA}+:\\
\begin{ProofRefA}{ ab2|: }\end{ProofRefA}
\par\kern\dp\strutbox\hrule\kern\dp\strutbox
\noindent\verb+\begin{ProofRefA}{ ab2 || }\end{ProofRefA}+:\\
\begin{ProofRefA}{ ab2 || }\end{ProofRefA}
\par\kern\dp\strutbox\hrule\kern\dp\strutbox
\noindent\verb+\begin{ProofRefA}{ ab2|:ab3 }\end{ProofRefA}+:\\
\begin{ProofRefA}{ ab2|:ab3 }\end{ProofRefA}
\par\kern\dp\strutbox\hrule\kern\dp\strutbox
\noindent\verb+\begin{ProofRefA}{ |: || || |: || || }\end{ProofRefA}+:\\
\begin{ProofRefA}{ |: || || |: || || }\end{ProofRefA}
\par\kern\dp\strutbox\hrule\kern\dp\strutbox
\noindent\verb+\begin{ProofRefA}{ ab1 || ab2|:ab3|:ab4 || ab5 || ab6 }\end{ProofRefA}+:\\
\begin{ProofRefA}{ ab1 || ab2|:ab3|:ab4 || ab5 || ab6 }\end{ProofRefA}
\par\kern\dp\strutbox\hrule\kern\dp\strutbox
\noindent\verb+\begin{ProofRefA}{ ab1 || ab2|:ab3 || ab4 |: ab5 || ab6 }\end{ProofRefA}+:\\
\begin{ProofRefA}{ ab1 || ab2|:ab3 || ab4 |: ab5 || ab6 }\end{ProofRefA}
\end{document}