本地修补 \newcommand 和 \newenvironment

本地修补 \newcommand 和 \newenvironment

我想定义一个环境本地补丁\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独立于组嵌套和环境嵌套。这可能会造成混淆。

相关内容