出于某种原因,我想要一个宏\BeginControl
,它\BeginControl{\environment}
相当于\begin{environment}
。我整理了一些代码。它通常可以工作,但我正在尝试解决星号环境的问题。
考虑以下代码。align*
环境的两个实例应该表现相同,但它会产生一个错误的错误。! LaTeX Error: \begin{align*} on input line 33 ended by \end{align*}.
问题出在哪里?
\documentclass{article}
\usepackage{amsmath}
\newcommand*{\cdef}{\newcommand*}
\cdef \Expanded [1]{%
\begingroup
\edef \x {%
\endgroup
#1%
}%
\x
}
\cdef \Apply [1]{%
\Expanded{\noexpand #1}%
}
\cdef \IgnoreNext [1]{%
% ignore next token
}
\cdef \MacroName [1]{%
\expandafter\IgnoreNext \string#1%
}
\cdef \BeginControl [1]{%
\Apply{\begin{\MacroName{#1}}}%
}
\begin{document}
\begin{align*}
A \\
B
\end{align*}
\BeginControl{\align*}
A \\
B
\end{align*}
\end{document}
更新:感谢答案,我发现我的宏\MacroName
不正确,因为它扩展为带有 catcode 12 而不是 11 的宏名称\string
。以下定义应该有效:
\let \Ex \expandafter
\cdef \ExEx {%
\Ex\Ex\Ex
}
\cdef \ExExArg [2]{%
\ExEx#1\ExEx{#2}%
}
\cdef \MacroName [1]{%
\ExExArg\scantokens{\Ex\IgnoreNext \string#1\noexpand}%
}
答案1
让我们看看会发生什么
\BeginControl{\align}
步骤 1:扩展\BeginControl
\Apply{\begin{\MacroName{\align}}}
第 2 步:扩展\Apply
\Expanded{\noexpand\begin{\MacroName{\align}}}
步骤 3:扩展\Expanded
\begingroup\edef\x{\endgroup\noexpand\begin{\MacroName{\align}}}\x
步骤4:\begingroup
执行;打开一个组并删除命令。
步骤 5:\edef
导致给定的完全扩展平衡文本。
子步骤5.1:\endgroup
不可扩展,将其推入待构建的token列表中。
子步骤 5.2:\noexpand
使下一个标记不可扩展;将其移除并推送到\begin
正在构建的标记列表中。
子步骤 5.3:{
不可展开,请推动它。
子步骤 5.4:扩展\MacroName
\expandafter\IgnoreNext\string\align
子步骤 5.5:\expandafter
扩展\string
并删除。请注意,\string\align
产生\align
字符所有具有类别代码12.
子步骤 5.6:扩展\IgnoreNext
;反斜杠已被删除。
子步骤 5.7-5.11:令牌align
不可扩展;将它们推送到正在构建的令牌列表中。
子步骤 5.12:}
不可扩展,请推动它
步骤 6:标记列表是\endgroup\begin{align}
;TeX 执行\def\x{\endgroup\begin{align}}
步骤 7:展开,用上面的标记列表\x
替换。\x
第八步:\endgroup
关闭已打开的群组,含义\x
忘记了。
步骤9:\begin{align}
执行。
看起来它应该能起作用;但可惜不能。
请记住,align
是具有类别代码 12 的字符串;当 LaTeX 执行时,\begin{align}
它会设置\@currenvir
为align
(所有类别代码 12);的执行\end{align}
会将参数与进行比较\@currenvir
,结果不同,因为\end{align}
字符的类别代码为 11。
您可以检查
\expandafter\begin\expandafter{\string minipage}{25pt}
xyz
\end{minipage}
给出相同的错误:只有类别代码 12,但这足以使和m
处的两个“字符串”有所不同。\begin
\end
错误已经解释过了;我不确定你采用如此复杂的方法的目的是什么。
一个办法。
\documentclass{article}
\usepackage{amsmath}
\makeatletter
\newcommand{\BeginControl}[1]{%
\begingroup
\edef\x{\expandafter\@gobble\string#1}%
\edef\x{\endgroup\noexpand\begin{\scantokens\expandafter{\x\noexpand}}}%
\x
}
\makeatother
\begin{document}
\BeginControl{\enumerate}
\item xyz
\end{enumerate}
\BeginControl{\align}
A \\
B
\end{align}
\BeginControl{\align*}
A \\
B
\end{align*}
\end{document}
使用通常的类别代码重新读取\scantokens
字符串。
答案2
您的问题是 LaTeX 检查了 中的 strigs+catcodes 的等价性\@endcheck
,因为\ifx
使用了 。正常类型的环境中有 catcode 为 11 的字母,其他字符为 12,但 生成的字符串\string
有 catcode 为 12 的字母。
您必须将字符串重新 catcode 为 catcodes 11。我建议基于以下内容使用类似的可扩展宏\ifcase
:
\documentclass{article}
\usepackage{amsmath}
\def\recatcode#1{\ifx#1\relax\else
\ifcase\numexpr`#1-`A\relax A\or B\or C\or D\or E\or F\or G\or H\or I\or
J\or K\or L\or M\or N\or O\or P\or Q\or R\or S\or T\or U\or V\or W\or
X\or Y\or Z\or#1\or#1\or#1\or#1\or#1\or#1\or
a\or b\or c\or d\or e\or f\or g\or h\or i\or
j\or k\or l\or m\or n\or o\or p\or q\or r\or s\or t\or u\or v\or w\or
x\or y\or z\else #1\fi
\expandafter\recatcode\fi
}
\def\BeginControl#1{\edef\x{\expandafter\ignoreone\string#1}%
\edef\x{\expandafter\recatcode\x\relax}%
\expandafter\begin\expandafter{\x}%
}
\def\ignoreone#1{}
\begin{document}
\begin{align*}
A \\
B
\end{align*}
\BeginControl{\align}
A \\
B
\end{align}
\end{document}