![逐部分定义命令](https://linux22.com/image/399021/%E9%80%90%E9%83%A8%E5%88%86%E5%AE%9A%E4%B9%89%E5%91%BD%E4%BB%A4.png)
这个问题是逐部分定义命令线程。假设我们有一个带有 1 个参数的命令。我们可以生成一个重新定义为的\foo
命令:如果的参数等于“aaa”,则执行“某些操作”;否则执行 \foo 之前应该执行的操作?我也想多次使用 \setfoo,所以\setfoo{aaa}{<some stuff>}
\foo
\foo
\setfoo{a}{part1}
\setfoo{b}{part2}
\setfoo{c}{part3}
\foo{a} \foo{b} \foo{c}
会产生
part1 part2 part3
此外,我希望能够使用\setfoo
inside enumerate
,然后使用\foo
inside 或 outside ,因此
\begin{enumerate}
\item First item. \setfoo{1}{part 1}
\setfoo{2}{part 2}
\item Second item.
\item Some text \setfoo{3}{part 3} Some text \foo{1}
\end{enumerate}
\foo{1} \foo{2} \foo{3}
会给
1. First item.
2. Second item.
3. Some text Some text part 1
part 1 part 2 part 3
我已经尝试了 egreg 对上一个问题的解决方案,但\foo
之后的所有 s\end{enumerate}
都是未定义的。MWE:
\documentclass{article}
\newcommand{\setfoo}[2]{%
\expandafter\newcommand\csname tomasz@#1\endcsname{#2}%
}
\makeatletter
\newcommand{\foo}[1]{%
\ifcsname tomasz@#1\endcsname
\expandafter\@firstoftwo
\else
\expandafter\@secondoftwo
\fi
{\csname tomasz@#1\endcsname}%
{BUMMER!}%
}
\makeatother
\begin{document}
\begin{enumerate}
\item First item. \setfoo{1}{part 1}
\setfoo{2}{part 2}
\item Second item.
\item Some text \setfoo{3}{part 3} Some text \foo{1}
\end{enumerate}
\foo{1} \foo{2} \foo{3}
\end{document}
得出的结果为:
1. First item.
2. Second item.
3. Some text Some text part 1
BUMMER! BUMMER! BUMMER!
(BUMMER!是以前没有使用过\foo{<x>}
的输出。)\setfoo{<x>}{<something>}
如果这可以使问题更容易,我只能使用数字作为的参数\foo
,但我会很感激一般的解决方案(当的参数\foo
是一些字母/数字的字符串时)。
答案1
只需将定义设为全局的:
\newcommand{\setfoo}[2]{%
\expandafter\gdef\csname tomasz@#1\endcsname{#2}%
}
\newcommand
在当前组中定义一个宏(因此显然在子组中)。当此组完成时,定义将丢失:宏定义将恢复到组启动之前的状态。在成功调用的情况下\newcommand
,先前的状态必然是“未定义”或\let
等于\relax
,但如果\renewcommand
在组内使用,则组结束时恢复的先前状态是命令在组启动之前的定义。示例:
\documentclass{article}
\begin{document}
\newcommand{\foo}{outside}
{ \renewcommand{\foo}{inside} \foo }
\foo
\end{document}
\newcommand
与和朋友(\newcommand*
、\renewcommand
、\providecommand
等)相反,\gdef
更新以下标记的含义全局:新的定义超越了所有群体。
请注意,除了语法之外,\gdef
和之间还有一些其他区别:\newcommand
用 定义的命令
\gdef
不是\long
,除非定义类似于\long\gdef\cmdname〈parameter text〉{〈replacement text〉}
(在下面的例子中,您需要用 替换\gdef
)\long\expandafter\gdef
。\long
宏接受包含\par
标记的参数,而其他宏在使用此类参数调用时会抛出错误(著名的“失控参数”错误消息)。\long
因此,不是 更具限制性,但这使在 (La)TeX 代码中查找错误变得更容易,假设人们已经决定\par
标记与所定义命令的参数无关(这意味着不应将命令定义为\long
;使用 LaTeX2e 内核框架,应将其定义为\newcommand*
)。\gdef
如果被要求,会很乐意重新定义相同的命令,而\newcommand
会拒绝这样做(它会抛出一个错误)。这种行为\gdef
可能对你的情况来说是一个理想的特性(?)。如果不是,很容易添加一个检查,如果命令已经定义,就会出错。
经过修复的示例的完整代码\gdef
:
\documentclass{article}
\newcommand{\setfoo}[2]{%
\expandafter\gdef\csname tomasz@#1\endcsname{#2}%
}
\makeatletter
\newcommand{\foo}[1]{%
\ifcsname tomasz@#1\endcsname
\expandafter\@firstoftwo
\else
\expandafter\@secondoftwo
\fi
{\csname tomasz@#1\endcsname}%
{BUMMER!}%
}
\makeatother
\begin{document}
\begin{enumerate}
\item First item. \setfoo{1}{part 1}
\setfoo{2}{part 2}
\item Second item.
\item Some text \setfoo{3}{part 3} Some text \foo{1}
\end{enumerate}
\foo{1} \foo{2} \foo{3}
\end{document}