我希望定义一个元命令,无论何时调用,它都会定义另一个命令的实例;但是这个内部命令必须包含一个渐进的数字(有点类似于编号方程、定理等的情况)。
但是,在我迄今为止的尝试中,数字似乎被评估得太晚了(在处理内部命令时,而不是在定义它时),因为所有内部命令都以计数器的最终值(偏差一,不少于一)而不是定义每个内部命令时的计数器值。
也许解释这个问题的最好方法是用一个带注释的小例子,它由两个文件组成。第一个文件应该被其他几个文件包含。第二个文件是包含第一个文件的几个其他文件之一。
第一个文件(testdef.tex):
% This file may be included by several other files.
% In this example, we are creating a number of proverb-like "rules"
% that other documents will need to refer to.
% All the rules are defined in this file, each with a RULENAME and a
% text. Each rule is given a number, starting from 1, based on the
% order in which they appear here. By defining a rule, we create a new
% internal macro \ruleRULENAME that expands to the text of the rule,
% prefixed by the rule number. The command \showrule{RULENAME}
% displays the rule in the approved way (say in a box) and defines the
% anchor for the label RULENAME: \ref{RULENAME} gives the rule number
% and \pageref{RULENAME} gives the page where \showrule{RULENAME} was
% first issued. Because \showrule{RULENAME} is the anchor for the
% matching label, you cannot issue \showrule{RULENAME} more than
% once. If you need to show the rule without (re)defining the label, use
% \showrulenolabel{RULENAME}.
\newcounter{rule}
\newcommand{\defrule}[2]{
\stepcounter{rule}
\expandafter\newcommand\csname rule #1\endcsname{\textbf{Rule \therule:} #2}
}
\newcommand{\showrulenolabel}[1]{
\noindent\framebox{\csname rule #1\endcsname}
}
\newcommand{\showrule}[1]{
\label{#1}
\refstepcounter{rule}
\showrulenolabel{#1}
}
% the actual rules are defined here
\defrule{vegetables}{Eat more vegetables}
\defrule{smile}{Smile}
\defrule{stairs}{Take the stairs}
第二个文件,包含第一个文件:
\documentclass{report}
\begin{document}
\input{testdef}
This is one of several documents that will include the rules file.
In here I should be able to display any rule by name, for example\ldots
\showrule{smile} (should be 2)
%The actual text of that rule is ``\rulesmile'' but I am not really supposed
%to be using that. Strangely, if I do, it says undefined control sequence.
The rules I know about are, in order:
\showrulenolabel{vegetables} (should be 1)
\showrulenolabel{smile} (should be 2)
\showrulenolabel{stairs} (should be 3)
and I should be able to say that
\end{document}
处理第二个文件会生成一个文档,其中所有规则的编号都为 4,而不是预期的 1、2、3。
我对 Latex 相当熟悉,但对普通的 Tex 不太熟悉,我怀疑这就是我无法得到正确答案的原因。我找到的最相关的建议来自另一个问题我尝试应用其中的一些,但是我对原始 Tex 命令的理解有限,导致我无法获得预期的结果。
答案1
你需要扩大定义你的宏的完整:这需要\edef
(一个 TeX 原语):
\newcommand{\defrule}[2]{%
\stepcounter{rule}%
\expandafter\edef\csname rule #1\endcsname{\noexpand\textbf{Rule \therule:} #2}%
}
由于这将扩大一切,你可能需要对 采取一些预防措施#2
。假设 e-TeX 可用(它在任何现代系统上都可以):
\newcommand{\defrule}[2]{%
\stepcounter{rule}%
\expandafter\edef\csname rule #1\endcsname{\noexpand\textbf{Rule \therule:}
\unexpanded{#2}}%
}
如果 e-TeX 不可用,则需要使用令牌寄存器进行更多工作。
答案2
免费\unexpanded
版本:
\newcommand{\defrule}[2]{%
\stepcounter{rule}%
\edef\temp{\noexpand\textbf{Rule \therule:}}%
\expandafter\newcommand\csname rule#1\expandafter\endcsname
\expandafter{\temp\space #2}}
这里我们使用\expandafter
just before\endcsname
来访问下一个将要扩展的元素\temp
(使用 before 定义\edef
,因此数字是正确的)。该\expandafter\endcsname
技巧在许多情况下非常方便。
这还有一个好处,我们可以执行\newcommand
命令是否尚未定义的检查。
如果担心某些控制序列被重新定义,那么可以使用适当的临时控制序列
\makeatletter
\newcommand{\defrule}[2]{%
\stepcounter{rule}%
\edef\@tempa{\noexpand\textbf{Rule \therule:}}%
\expandafter\newcommand\csname rule#1\expandafter\endcsname
\expandafter{\@tempa\space #2}}
\makeatother
控制序列\@tempa
多次使用仅是临时使用,使用后任何人都不应该依赖它的含义。
它是如何工作的?首先\defrule
收集它的参数;假设调用的是\defrule{smile}{Just smile!}
。
第一个任务是步进计数器rule
。假设当前值变为 42。然后我们执行
\edef\@tempa{\noexpand\textbf{Rule \therule:}}
在当前条件下相当于
\def\@tempa{\textbf{Rule 42:}}
因为使(仅一次)\noexpand
的可扩展性无效。\textbf
现在到了有趣的部分:
\expandafter\newcommand\csname <token list>\endcsname
首先进行扩展\csname
,以便\newcommand
呈现一个可以操作的真实标记。<token list>
一直展开到包括立即起作用的,触发括号后\expandafter
的扩展。然后开始行动并发现\@tempa
\newcommand
\newcommand\rulesmile{\textbf{Rule 42:}\space Just smile!}
瞧。