我想定义一个环境本地补丁\newcommand
和\newenvironment
系列命令,在每个新定义前加上一些字符串作为前缀,例如
\begin{namespace}{hello}
\newcommand{\world}{Hello, World}
\end{namespace}
\world % undefined
\helloworld % prints "Hello, World"
我怎样才能做到这一点?能我这么做吗?
答案1
这应该涵盖\(re)newcommand
、\providecommand
、\DeclareRobustCommand
和\(re)newenvironment
。它使用两个命令\BeginNamespace{<namespace>}
和\EndNamespace
(不能嵌套),而不是环境语法,因为在环境中定义的命令在环境结束后会丢失。
这是通过重新定义抓取命令名称的命令来实现的,并<namespace>
在它们前面使用添加\csname<namespace>\cs_to_str:N #1\endcsname
。
\documentclass{article}
\makeatletter
\ExplSyntaxOn
\cs_new_eq:NN \CStostr \cs_to_str:N
\ExplSyntaxOff
\newif\if@namespace
\newcommand\BeginNamespace[1]{%
\if@namespace \ERROR@cannot@nest@namespaces \else
\@namespacetrue
\let\NS@new@command\new@command
\let\NS@renew@command\renew@command
\let\NS@declare@robustcommand\declare@robustcommand
\let\NS@new@environment\new@environment
\let\NS@renew@environment\renew@environment
\def\new@command##1{%
\expandafter\NS@new@command\csname#1\CStostr##1\endcsname}
\def\renew@command##1{%
\expandafter\NS@renew@command\csname#1\CStostr##1\endcsname}
\def\declare@robustcommand##1{%
\expandafter\NS@declare@robustcommand\csname#1\CStostr##1\endcsname}
\def\new@environment##1{\NS@new@environment{#1##1}}
\def\renew@environment##1{\NS@renew@environment{#1##1}}%
\fi}
\newcommand\EndNamespace{%
\if@namespace
\let\new@command\NS@new@command
\let\renew@command\NS@renew@command
\let\new@environment\NS@new@environment
\let\renew@environment\NS@renew@environment
\@namespacefalse
\else \ERROR@extra@EndNamespace \fi}
\makeatother
\newcommand{\world}{WORLD}
\BeginNamespace{hello}
\newcommand{\world}{Hello, World}
\EndNamespace
\begin{document}
\world % prints "WORLD"
\helloworld % prints "Hello, World"
\end{document}
答案2
我可以提供一个宏\CsNameToCsToken
,它可以帮助在不修补\newcommand
/ 的情况下完成任务。除此之外,在使用/\newenvironment
以外的定义命令时它可能很有用:\newcommand
\newenvironment
句法:
\CsNameToCsToken⟨stuff not in braces⟩{⟨NameOfCs⟩}
→
⟨stuff not in braces⟩\NameOfCs
⟨stuff not in braces⟩
例如可以是\newcommand*
或\DeclareRobustCommand
或\NewDocumentCommand
或或\global\long\outer\def
或\meaning
或\string
或\global\let
等等。
⟨stuff not in braces⟩
可能为空。⟨stuff not in braces⟩
空意味着仅调用控制序列。
您可以嵌套\CsNameToCsToken
:
\CsNameToCsToken\CsNameToCsToken\global\let{foo}={bar}
产量\CsNameToCsToken\global\let\foo={bar}
产量\global\let\foo=\bar
。
\begingroup
\makeatletter
\@firstofone{%
\endgroup
\@ifdefinable\Stopromannumeral{\chardef\Stopromannumeral=`\^^00}%
\@ifdefinable\CsNameToCsToken{%
\long\def\CsNameToCsToken#1#{\romannumeral\InnerCsNameToCsToken{#1}}%
}%
\newcommand\InnerCsNameToCsToken[2]{%
\expandafter\exchange\expandafter{\csname#2\endcsname}{\Stopromannumeral#1}%
}%
\newcommand\exchange[2]{#2#1}%
}%
例如,你可以定义\newcommand\namespace{hello}
并执行
\CsNameToCsToken\newcommand{\namespace world}{Hello, World}
用于定义控制字标记\helloworld
。
\documentclass{article}
\begingroup
\makeatletter
\@firstofone{%
\endgroup
\@ifdefinable\Stopromannumeral{\chardef\Stopromannumeral=`\^^00}%
\@ifdefinable\CsNameToCsToken{%
\long\def\CsNameToCsToken#1#{\romannumeral\InnerCsNameToCsToken{#1}}%
}%
\newcommand\InnerCsNameToCsToken[2]{%
\expandafter\exchange\expandafter{\csname#2\endcsname}{\Stopromannumeral#1}%
}%
\newcommand\exchange[2]{#2#1}%
}%
\begin{document}
\newcommand\namespace{hello}
\CsNameToCsToken\newcommand{\namespace world}{Hello, World}
\helloworld
\texttt{\string\helloworld: \meaning\helloworld}
\texttt{\string\world: \meaning\world}
\end{document}
此外,您还可以通过and定义用于嵌套命名空间的堆栈,以便宏产生当前命名空间的名称:\BeginNamespace{⟨name of namespace⟩}
\EndNamespace
\namespace
\documentclass{article}
\makeatletter
\newcommand*\namespace{}%
\newcommand*\EndNamespace{\gdef\namespace{}}%
\newcommand*\BeginNamespace[1]{%
\toks@\expandafter{\expandafter\gdef\expandafter\namespace\expandafter{\namespace}}%
\toks@\expandafter{\the\expandafter\toks@\expandafter\gdef\expandafter\EndNamespace\expandafter{\EndNamespace}}%
\xdef\EndNamespace{\the\toks@}%
\gdef\namespace{#1}%
\ignorespaces
}%
\makeatother
\begin{document}
\show\namespace
\show\EndNamespace
\BeginNamespace{A}
\show\namespace
\show\EndNamespace
\BeginNamespace{B}
\show\namespace
\show\EndNamespace
\BeginNamespace{C}
\show\namespace
\show\EndNamespace
\EndNamespace
\show\namespace
\show\EndNamespace
\EndNamespace
\show\namespace
\show\EndNamespace
\EndNamespace
\show\namespace
\show\EndNamespace
\EndNamespace
\end{document}
将上述示例保存为test.tex
并编译会在终端上产生以下消息:
This is pdfTeX, Version 3.14159265-2.6-1.40.21 (TeX Live 2020) (preloaded format=pdflatex)
restricted \write18 enabled.
entering extended mode
(./test.tex
LaTeX2e <2020-10-01> patch level 4
L3 programming layer <2021-02-18>
(/usr/local/texlive/2020/texmf-dist/tex/latex/base/article.cls
Document Class: article 2020/04/10 v1.4m Standard LaTeX document class
(/usr/local/texlive/2020/texmf-dist/tex/latex/base/size10.clo))
(/usr/local/texlive/2020/texmf-dist/tex/latex/l3backend/l3backend-pdftex.def)
(./test.aux)
> \namespace=macro:
->.
l.17 \show\namespace
?
> \EndNamespace=macro:
->\gdef \namespace {}.
l.18 \show\EndNamespace
?
> \namespace=macro:
->A.
l.22 \show\namespace
?
> \EndNamespace=macro:
->\gdef \namespace {}\gdef \EndNamespace {\gdef \namespace {}}.
l.23 \show\EndNamespace
?
> \namespace=macro:
->B.
l.27 \show\namespace
?
> \EndNamespace=macro:
->\gdef \namespace {A}\gdef \EndNamespace {\gdef \namespace {}\gdef \EndNamespa
ce {\gdef \namespace {}}}.
l.28 \show\EndNamespace
?
> \namespace=macro:
->C.
l.32 \show\namespace
?
> \EndNamespace=macro:
->\gdef \namespace {B}\gdef \EndNamespace {\gdef \namespace {A}\gdef \EndNamesp
ace {\gdef \namespace {}\gdef \EndNamespace {\gdef \namespace {}}}}.
l.33 \show\EndNamespace
?
> \namespace=macro:
->B.
l.37 \show\namespace
?
> \EndNamespace=macro:
->\gdef \namespace {A}\gdef \EndNamespace {\gdef \namespace {}\gdef \EndNamespa
ce {\gdef \namespace {}}}.
l.38 \show\EndNamespace
?
> \namespace=macro:
->A.
l.42 \show\namespace
?
> \EndNamespace=macro:
->\gdef \namespace {}\gdef \EndNamespace {\gdef \namespace {}}.
l.43 \show\EndNamespace
?
> \namespace=macro:
->.
l.47 \show\namespace
?
> \EndNamespace=macro:
->\gdef \namespace {}.
l.48 \show\EndNamespace
?
> \namespace=macro:
->.
l.52 \show\namespace
?
> \EndNamespace=macro:
->\gdef \namespace {}.
l.53 \show\EndNamespace
?
(./test.aux) )
No pages of output.
Transcript written on test.log.
结合这两个想法,您可以\CsNameToCsToken
在名称空间内使用它来将当前定义的 -macro 扩展\namespace
为控制序列的名称 - 这次不是将\toks@
-assignments与-expansion\xdef
结合起来进行重新定义:\unexpanded
\romannumeral
\EndNamespace
\documentclass{article}
\makeatletter
\@ifdefinable\Stopromannumeral{\chardef\Stopromannumeral=`\^^00}%
\@ifdefinable\CsNameToCsToken{%
\long\def\CsNameToCsToken#1#{\romannumeral\InnerCsNameToCsToken{#1}}%
}%
\newcommand\InnerCsNameToCsToken[2]{%
\expandafter\exchange\expandafter{\csname#2\endcsname}{\Stopromannumeral#1}%
}%
\newcommand\exchange[2]{#2#1}%
\newcommand*\namespace{}%
\newcommand*\EndNamespace{\gdef\namespace{}}%
\newcommand*\BeginNamespace[1]{%
\xdef\EndNamespace{%
\unexpanded\expandafter{\romannumeral
\expandafter\exchange\expandafter{\expandafter\gdef\expandafter\EndNamespace\expandafter{\EndNamespace}}%
{\expandafter\Stopromannumeral\expandafter\gdef\expandafter\namespace\expandafter{\namespace}}%
}%
}%
\xdef\namespace{\unexpanded{#1}}%
\ignorespaces
}%
\makeatother
\begin{document}
\BeginNamespace{A}
\CsNameToCsToken\newcommand*{Hello\namespace}{Hello, A!}%
\newenvironment{\namespace Environment}{This is the start of AEnvironment.}{This is the end of AEnvironment.}
\BeginNamespace{B}
\CsNameToCsToken\newcommand*{Hello\namespace}{Hello, B!}%
\newenvironment{\namespace Environment}{This is the start of BEnvironment.}{This is the end of BEnvironment.}
\BeginNamespace{C}
\CsNameToCsToken\newcommand*{Hello\namespace}{Hello, C!}%
\newenvironment{\namespace Environment}{This is the start of CEnvironment.}{This is the end of CEnvironment.}
\texttt{\CsNameToCsToken\string{Hello\namespace}: \CsNameToCsToken\meaning{Hello\namespace}}%
\EndNamespace
\texttt{\CsNameToCsToken\string{Hello\namespace}: \CsNameToCsToken\meaning{Hello\namespace}}%
\EndNamespace
\texttt{\CsNameToCsToken\string{Hello\namespace}: \CsNameToCsToken\meaning{Hello\namespace}}%
\EndNamespace
\hrulefill
\texttt{\string\HelloA: \meaning\HelloA}
\texttt{\string\HelloB: \meaning\HelloB}
\texttt{\string\HelloC: \meaning\HelloC}
\begin{AEnvironment} \end{AEnvironment}
\begin{BEnvironment} \end{BEnvironment}
\begin{CEnvironment} \end{CEnvironment}
\end{document}
\BeginNamespace
..\EndNamespace
独立于组嵌套和环境嵌套。这可能会造成混淆。