我知道如何定义宏如下
\newcommand{\mycommand}{ here my macro commands }
但我想创建一个定义宏的环境,如下所示
\begin{definemycommand}{\mycommand}
here my macro coomands
\end{definemycommand}
以便结果是一样的。
我尝试了一些非常愚蠢的事情,例如
\newenvironment{definemycommand}[1]{\newcommand{#1}\{ }{ \} }
或者
\newenvironment{definemycommand}[1]{\newcommand{#1}{ }{ } }
但显然这行不通,因为“{”和“}”无法像我希望的那样被识别。我怀疑有一个正确的方法来做到这一点。
答案1
对于这种用法,没有必要省略括号,例如:
\newenvironment{definemycommand}[1]{\newcommand{#1}\{ }{ \} }
那么,让我们看看你的第二次尝试:
\newenvironment{definemycommand}[1]{\newcommand{#1}{ }{ } }
这是不正确的,因为\newcommand
不需要那么多参数(第二个参数{ }
会留在输入流中,后面跟着一个可能不需要的空格标记)。更糟糕的是,\newenvironment
需要两个论点在方括号内的参数数量后面的花括号中:一个用于开始,一个用于结束。
局部定义
\newcommand
执行本地定义,因此,您不能\mycommand
在环境之外使用以下内容:
\documentclass{article}
\newenvironment{definemycommand}[1]
{\newcommand{#1}{here my macro commands}}
{}
\begin{document}
\begin{definemycommand}{\mycommand}
\mycommand
\end{definemycommand}
%\mycommand % ERROR: Undefined control sequence.
\end{document}
全局定义
如果您希望在环境结束后也能使用该命令,则可以使用\gdef
。但是与 有一个明显的区别\newcommand
(其中包括):\gdef
不检查命令是否已存在。因此,它会在不通知的情况下覆盖现有的同名命令。如果您不想要这样,您可以使用\ifdefined ... \fi
或\ifcsname ... \endcsname ... \fi
(\else
在每种情况下都是可选的)。
\documentclass{article}
\newenvironment{definemycommand}[1]
{\gdef#1{here my macro commands}}
{}
\begin{document}
\begin{definemycommand}{\mycommand}
\mycommand
\end{definemycommand}
\bigskip
\mycommand % no problem
\end{document}
使用环境主体进行宏替换文本
如果你想使用环境主体作为宏替换文本,你可以这样做Phelype Oleinik 的回答或者使用environ
包。我使用\unexpanded\expandafter{\BODY}
以便只展开一次特殊宏(该宏由包中\BODY
定义)。\NewEnviron
environ
\documentclass{article}
\usepackage{environ}
\NewEnviron{definemycommand}[1]
{\xdef#1{\unexpanded\expandafter{\BODY}}}
{}
\begin{document}
\begin{definemycommand}{\mycommand}
here my macro commands
\end{definemycommand}
\mycommand
\end{document}
与第一个示例的输出相同:
注意环境主体开头和结尾可能存在的空格。使用诸如或 之X\mycommand X
类的测试来检查环境定义的命令的精确行为或定义。在这种情况下,包似乎默认删除了空格,因此这种潜在的陷阱不适用。\show\mycommand
\meaning\mycommand
definemycommand
environ
使用改进etoolbox
以下是之前代码的一个变体:
将命令名称作为参数没有前导反斜杠(我使用
etoolbox
,\csxdef
其内部依赖于\csname ... \endcsname
和\xdef
);如果命令已经定义,则打印一条错误消息(这用于
\ifcsdef
执行测试;当然,如果您希望能够重新定义该命令,只需删除测试\ifcsdef
和\errmessage
调用即可);使用
etoolbox
的\expandonce{...}
命令作为 的语法糖\unexpanded\expandafter{...}
。
\documentclass{article}
\usepackage{environ}
\usepackage{etoolbox}
\NewEnviron{definemycommand}[1]
{%
\ifcsdef{#1}
{\errmessage{%
Command \expandafter\string\csname#1\endcsname\space already defined}%
}
{\csxdef{#1}{\expandonce{\BODY}}}%
}
{}
\begin{document}
\newcommand*{\whatever}{foo bar}
\begin{definemycommand}{mycommand}
\whatever\space here my macro commands
\end{definemycommand}
\show\mycommand % ->\whatever \space here my macro commands.
X\mycommand X
% ERROR: Command \mycommand already defined.
% \begin{definemycommand}{mycommand}
% here my macro commands
% \end{definemycommand}
\end{document}
的输出\show\mycommand
,即:
> \mycommand=macro:
->\whatever \space here my macro commands.
\mycommand
证明在定义宏(例如)时,环境主体中使用的宏不会展开。这正是\unexpanded
没有 的示例中的 的目的etoolbox
,或\expandonce
在此示例中。环境主体中给出的此类宏将在\mycommand
多次展开时展开(大概是作为排版的一部分,但这实际上取决于您将在何处以及如何使用它)。这通常是一个理想的属性,这就是我以这种方式实现事物的原因(否则,简单的\xdef
或\csxdef
就可以完成工作)。
答案2
使用 的xparse
参数b
类型来获取环境的主体:
\documentclass{article}
\usepackage{xparse}
\NewDocumentEnvironment{definemycommand}{mb}
{\gdef#1{#2}}% #1 is the macro name, and #2 is the env body
{}
\begin{document}
\begin{definemycommand}{\test}
here my macro coomands
\end{definemycommand}
\texttt{\meaning\test}
\end{document}
请注意,您必须使用\gdef
(或类似的东西),否则环境结束时定义就会丢失。
答案3
我不确定这有什么用。无论如何,你可以这样做。
\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\tl_new:N \g__definecommand_temp_tl
\NewDocumentEnvironment{definecommand}{mO{0}o+b}
{
\IfNoValueTF{#3}
{
\tl_gset:Nn \g__definecommand_temp_tl { \newcommand{#1}[#2]{#4} }
}
{
\tl_gset:Nn \g__definecommand_temp_tl { \newcommand{#1}[#2][#3]{#4} }
}
\group_insert_after:N \g__definecommand_temp_tl
}
{}
\ExplSyntaxOff
\begin{document}
\begin{definecommand}{\fooA}
this has no arguments
\end{definecommand}
\begin{definecommand}{\fooB}[1]
this has one argument #1
\end{definecommand}
\begin{definecommand}{\fooC}[2][foo]
this has one mandatory argument #2 and an optional argument #1
\end{definecommand}
.\fooA.
.\fooB{xyz}.
.\fooC{uvw}.
.\fooC[x]{uvw}.
\end{document}