我热衷于分解,讨厌代码重复。因此,我尝试定义一个宏,它为我定义所有常见的数学集命令(\C
、\N
等),并创建了一个递归函数,启发了\slowRomannumeral
等等),并从这些命令中创建了一个启发自第 24 页TeX 和 LaTeX 编程笔记。
所以我首先做了这个:
\def\defsets#1{\defnextset #1@}
\def\defnextset#1{\ifx @#1 \else
\expandafter\newcommand\csname #1\endcsname{\mathbb{#1}}
\expandafter\defnextset
\fi}
\defsets{RDNZQC}
但是我很快意识到我的数学运算符需要类似的东西,完全相同,只是用 而\DeclareMathOperator
不是\newcommand
和不带\mathbb
,所以我定义了一个\defset
命令并尝试使我的主要宏高阶:
\newcommand{\defset}[1]{\expandafter\newcommand\csname #1\endcsname{\mathbb{#1}}}
\def\defsets#1{\defnext\defset #1@}
\def\defnext#1#2{\ifx @#2 \else
#1#2\relax
\expandafter\defnext#1
\fi}
\defsets{RDNZQC}
我检查了很多东西:我把一个\relax
否则,由于未知的原因,它会尝试重新定义 expandafter,第一个\newcommand
做工作,#1#2
应该工作,并且使用\def
而不是\newcommand*
定义\defset
我得到以下错误:
ERROR: Missing \endcsname inserted.
--- TeX said ---
<to be read again>
\let
l.68 \defsets{RDNZQC}
并添加一个\expandafter
与实际当前代码相同的代码\newcommand*
(以及与之相同的所有组合):无限循环。
那么如何使该代码工作呢?所以我可以做一个\defop
并对我的数学运算符做同样的事情,并使用括号之间的每个运算符命令进行一些代码技巧(因为这实际上目前适用于单个字符)。
答案1
您不能通过一个 跳过多个标记\expandafter
。您的问题还有更多解决方案。例如,您可以\let
首先设置使用的 def-method:
\def\defset#1{\expandafter\def\csname#1\endcsname{\mathbb{#1}}}
\def\defsets#1{\let\defnextA=\defset \defnext #1@}
\def\defnext#1{\ifx @#1\else
\defnextA{#1}%
\expandafter\defnext
\fi}
\defsets{RDNZQC}
第二:您可以重复 def 方法,#1
但多一个\expandafter
:
\def\defset#1{\expandafter\def\csname#1\endcsname{\mathbb{#1}}}
\def\defsets#1{\defnext\defset #1@}
\def\defnext#1#2{\ifx @#2\else
#1{#2}%
\expandafter\defnext\expandafter#1%
\fi}
\defsets{RDNZQC}
答案2
你需要扩大\fi
\RequirePackage{amsmath}
\makeatletter
\newcommand{\defset}[1]{\expandafter\newcommand\csname #1\endcsname{\mathbb{#1}}}
\def\defsets#1{\defnext\defset #1@}
\def\defnext#1#2{\ifx @#2%
\expandafter\@gobble
\else
#1{#2}%
\expandafter\@firstofone
\fi
{\defnext#1}}
\defsets{RDNZQ{Zzz}C}
\show\Z
\show\Zzz
\newcommand*{\defop}[1]{\expandafter\DeclareMathOperator\csname #1\endcsname{#1}}
\defnext\defop {Vect}{Spec}@
{\let\protect\show\Vect}
\stop
生产
LaTeX2e <2016/03/31>
Babel <3.9q> and hyphenation patterns for 81 language(s) loaded.
> \Z=\long macro:
->\mathbb {Z}.
l.15 \show\Z
?
> \Zzz=\long macro:
->\mathbb {Zzz}.
l.16 \show\Zzz
?
> \Vect =\long macro:
->\qopname \newmcodes@ o{Vect}.
\Vect ->\protect \Vect
l.23 {\let\protect\show\Vect
}
?
请注意,您需要重新支撑#2
以{#2}
支持多个标记参数。
答案3
问题当然在于\expandafter\defnext#1
试图扩展所替换的内容#1
而不是预期的内容\fi
。
如果您讨厌重复代码,那么您一定会喜欢这个,其中\defsets
和\defops
基本上都是单行代码。
\documentclass{article}
\usepackage{amsmath}
\usepackage{xparse}
\ExplSyntaxOn
\NewDocumentCommand{\defsets}{m}
{
\tl_map_inline:nn { #1 }
{
\cs_new_protected:cpn { ##1 } { \mathbb{##1} }
}
}
\NewDocumentCommand{\defops}{m}
{
\clist_map_inline:nn { #1 }
{
\galex_declaremathoperator:cn { ##1 } { ##1 }
}
}
\cs_set_eq:NN \galex_declaremathoperator:Nn \DeclareMathOperator
\cs_generate_variant:Nn \galex_declaremathoperator:Nn { c }
\ExplSyntaxOff
\defsets{RDNZQC}
\defops{Vect,Spec}
\begin{document}
\texttt{\meaning\R}
\texttt{\meaning\Q}
\texttt{\meaning\Spec}
\texttt{\expandafter\meaning\csname Spec \endcsname}
\end{document}
只有一个\expandafter
能表明\Spec
做对了。编码部分没有。
我本可以用
\exp_args:Nc \DeclareMathOperator { ##1 } { ##1 }
而不是定义\galex_declaremathoperator:Nn
和其变体。但这样一切都更清晰,也符合建议。
得到同样的结果\@tfor
:
\makeatletter
%\let\@xp\expandafter % already done by amsmath
\newcommand{\defsets}[1]{%
\@tfor\next:=#1\do{%
\@xp\newcommand\csname\next\@xp\endcsname\@xp{\@xp\mathbb\@xp{\next}}%
}%
}
\newcommand{\defops}[1]{%
\@tfor\next:=#1\do{%
\@xp\DeclareMathOperator\csname\next\@xp\endcsname\@xp{\next}%
}%
}
\makeatother
\defsets{RDNZQC}
\defops{{Vect}{Spec}}
该\@tfor
函数本质上是变体中技巧的抽象版本\slowRomannumeral
:它每次使用在:=
和之间传递的标记列表中的一个项目\do
,执行\def\next{<item>}
(可以是任何标记)。主要区别恰恰在于此:如果我们不希望它被纳入和的定义中,\next
我们需要扩展它。因此需要长链。\next
\N
\expandafter
物品在此上下文中是一个单个标记或一个支撑组。
方法\tl_map_inline:nn
是很多更好,因为当前项目可以“按字面意思”使用#1
(因此在定义主体中必须调用它##1
)。
答案4
仿佛战斗过后醒来,一切尘埃已落定……
\documentclass{article}
\usepackage{amsmath}
\usepackage{xinttools}
\makeatletter
\newcommand\defsets [1]
{\xintFor* ##1 in {#1} \do {\@namedef{##1}{\mathbb{##1}}}}
\makeatother
\newcommand\defops [1]
{\xintFor ##1 in {#1} \do {\expandafter
\DeclareMathOperator\csname ##1\endcsname{##1}}}
\defsets{RDNZQC}
\defops{Vect, Spec}
\begin{document}
\texttt{\meaning\R}
\texttt{\meaning\Q}
\texttt{\meaning\Spec}
\texttt{\expandafter\meaning\csname Spec \endcsname}
\end{document}