存储未扩展的宏定义(包括 #1、#2、...)以供日后使用(现在包括完整用例)

存储未扩展的宏定义(包括 #1、#2、...)以供日后使用(现在包括完整用例)

初步信息

我正在开发一个新的 LaTeX 软件包,以使范围更加灵活。为此,我将在后台捕获并重新引入用户定义的命令。

(有关此包的全面描述,请跳至动机部分。)

  • 对于存储和检索,我使用优秀的datatool包裹。
  • 我会在某些时候整合这个功能\newcommand自身,但为了简化我的问题,我将定义\snewcommand宏。
  • 此代码不会显示范围内容。我正在将其简化为手头的问题。

我目前拥有的

\usepackage{xparse}
\usepackage{datatool}
\usepackage{etextools}

\makeatletter

\newcommand{\sc@db@name}{scopedcommands}

\DTLnewdb{\sc@db@name}

\newcommand{\sc@db@assignlist}{
    \sc@db@col@index      =index,
    \sc@db@col@star       =star,
    \sc@db@col@command    =command,
    \sc@db@col@arguments  =arguments,
    \sc@db@col@default    =default,
    \sc@db@col@definition =definition%
}

\newcounter{sc@defcount}

\DeclareDocumentCommand{\snewcommand}{s+mo+o+m}{%
    \addtocounter{sc@defcount}{1}%
    \DTLnewrow{\sc@db@name}%
    \dtlexpandnewvalue%
        \DTLnewdbentry{\sc@db@name}{index}    {\thesc@defcount}%
        \DTLnewdbentry{\sc@db@name}{star}     {\IfBooleanT{#1}{*}}%
        \DTLnewdbentry{\sc@db@name}{command}  {\noexpand#2}%
        \DTLnewdbentry{\sc@db@name}{arguments}{\IfNoValueF{#3}{[#3]}}%
    \dtlnoexpandnewvalue%
        \IfValueTF{#4}{\DTLnewdbentry{\sc@db@name}{default}{[#4]}}%
                      {\DTLnewdbentry{\sc@db@name}{default}{}}%
        \DTLnewdbentry{\sc@db@name}{definition}{#5}%
}

\newcommand{\definestoredcommand}[1]{%
    \expandnext{\DTLassign{\sc@db@name}{#1}}{\sc@db@assignlist}%
    %
    \expandnext{%
    \expandaftercmds{%
    \expandaftercmds{%
    \expandnext{%
    \expandaftercmds{%
        \newcommand%
    }{%
        \sc@db@col@star%
    }}%
        {\sc@db@col@command}%
    }{%
        \sc@db@col@arguments%
    }}{%
        \sc@db@col@default%
    }}%
        {\sc@db@col@definition}
}

\makeatother

它对于没有参数的命令非常有效:

\def\testfoo{foo}
\snewcommand*{\foo}{\testfoo}     % index 1
\snewcommand*{\foo}{\testfoo bar} % index 2

\definestoredcommand{1}
\foo % foo

\let\foo\relax
\definestoredcommand{2}
\foo % foobar

问题

但是,如果我添加参数并实际在命令定义中使用它们,我会收到来自最深层的错误datatool:“\@dtl@tmp 定义中的参数编号非法。”

显然,我应该在命令定义中转义井号字符。

问题

  1. 我怎样才能转义任何可能的哈希字符,以便它们暂时存储在数据库中,然后取消转义它们,以便它们能够完成它们的工作?
  2. 还有什么可能出错的地方吗?

动机(新信息)

我应该向你展示一下这个包完成后应该做什么。这是我写博士论文所需要的东西,但我认为它对其他 TeXers 可能有用。

基本

在其最简单的形式中,它允许您多次打开命名范围,并让引用环境从一个范围延续到另一个范围,但不能在两者之间延续。强制参数包含名称:

\begin{scope}{s1}
    \newcommand{\foo}{FOO}
    \foo   % FOO
\end{scope}

\foo   % <error: undefined>

\begin{scope}{s1}
    \foo   % FOO
\end{scope}

(到目前为止一切已正常。)

当然,嵌套作用域的行为与预期一致。但作用域也可以表现得像嵌套的,但文本上是分开的,这就是这个包的真正威力所在。您可以通过在可选参数中指定以逗号分隔的超级作用域列表来实现这一点:

\begin{scope}{s1}
    \newcommand{\foo}{FOO}

    \begin{scope}{s1sub}
        \newcommand{\baz}{BAZ}
    \end{scope}

    \baz   % <error: undefined>
\end{scope}

\begin{scope}[s1sub]{s2}
    \newcommand{\bar}{\foo BAR}
    \bar   % FOOBAR
    \baz   % BAZ
\end{scope}

\begin{scope}{s1}
    \bar   % <error: undefined>
    \baz   % <error: undefined>
\end{scope}

该包还允许您执行通常无法使用作用域执行的其他操作:具有部分嵌套层次结构,即从两个或多个不相关的作用域继承命令。一些先发制人的保证:通过多条路径继承的单源命令不会造成麻烦:

\begin{scope}{s1}
    \newcommand{\foo}{FOO}
\end{scope}

\begin{scope}[s1]{s2}
    \newcommand{\bar}{\foo BAR}
\end{scope}

\begin{scope}[s1]{s3}
    \newcommand{\baz}{\foo BAZ}
    \bar   % <error: undefined>
\end{scope}

\begin{scope}[s2,s3]{s4}
    \foo   % FOO   <no problem>
    \bar   % FOOBAR
    \baz   % FOOBAZ
\end{scope}

最后但同样重要的一点是,当您以这种方式“加入”两个范围时,您可以完全控制如何解决它们的引用环境之间的任何冲突:

\begin{scope}{s1}
    \newcommand{\foobar}{FOO}
\end{scope}

\begin{scope}{s2}
    \newcommand{\foobar}{BAR}
\end{scope}

\begin{scope}[s1,s2]{s3}
    % <no problem so far>
    \foobar   % <error: ambiguous>
    \newcommand{\foobar}{...}   % <error: already defined>
    \renewcommand{\foobar}{\from{s1}\foobar \from{s2}\foobar}
    \foobar   % FOOBAR
\end{scope}

更多

好消息是,你可以选择通过在现有构造中嵌入范围来消除所有开销。对于我预期的用例:

  • 环境document形成根范围
  • 每个chapter/ section/ 等都形成一个正确嵌套的作用域,其名称源自其\label
  • \label采用可选参数指定超级作用域

我的用例

我的博士论文将由一个正式的框架组成(增量建模:查找相关出版物这里),逐步介绍新的定义/定理,以先前的定义/定理为基础。这篇论文有部分推荐阅读顺序。一些独立的概念建立在第一章的基础上。后面的章节可能会结合这些概念。基于这个包,我可以确保:

  • 对定义/定理/符号的引用仅出现在部分阅读顺序中比其起源更晚的位置,
  • 新引入的名称和符号在其分支内是明确的,
  • 符号始终一致,并且
  • 符号可以自动提取并放入
    • 句子片段,例如“对于任何增量 $x, y \in D$”和
    • 反映阅读顺序的词汇表。

例子:

\chapter{Identity Function} \label{identity}

    \newcommand*{\i}{I}

    $\i$ is a function such that $\i(x) = x$.

\chapter{Derivatives} \label{derivatives}

    \newcommand*{\d}[1]{#1'}

    Given function $f$, $\d{f}$ is the derivative of $f$.

    % no clue about \i here

\chapter{Boring Conclusion} \label[identity,derivatives]{boring}

    Taking the information from chapters \ref{identity}
    and \ref{derivatives}, we can now see that:

    $\forall x \in \mathbb{R}: \d{\i}(x) = 1$

如您所见,我主要感兴趣的是部分顺序。但我选择让这个包更加通用,原因有二:

  • 它可能对其他人有用,也许能达到我还没有想到的目的,
  • 转换引用环境的部分有序范围可以看作增量模型中的增量。换句话说,这对我的博士研究来说很有趣。这个包的描述实际上将成为我论文的一部分,并在框架内形式化。我喜欢自我引用。

答案1

在上面的评论中,egreg 向我展示了我把问题复杂化了。我现在通过将每个新定义存储在一个唯一命名的全局宏中来解决问题。数据库现在只将用户定义的 <scope-name, macro-name> 对映射到包含适当定义的全局宏的名称。这是我目前拥有的代码。它包括我上面动机中的一些范围内容:

\documentclass{article}

\usepackage{xparse}
\usepackage{datatool}
\usepackage{etextools}
\usepackage{letltxmacro}

\makeatletter

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Global versions of \newcommand, etc. I needed this anyway, might as well
% make it complete.

\def\gc@common{%
    {%
        \LetLtxMacro{\gc@temp}{\relax}%
        \begingroup\edef\x{\endgroup%
            \noexpand\newcommand%
            \IfBooleanT{##1}{*}%
            {\noexpand\gc@temp}%
            \IfNoValueF{##3}{[##3]}%
            \IfNoValueF{##4}{[\unexpanded{##4}]}%
            {\unexpanded{##5}}%
        }\x%
        \GlobalLetLtxMacro{##2}{\gc@temp}%
    }%
}

% TODO: Yeah, they do the same thing now. Fix their behavior.
\expandnext{\DeclareDocumentCommand{\gensurecommand} {s+mo+o+m}}{\gc@common}
\expandnext{\DeclareDocumentCommand{\gnewcommand}    {s+mo+o+m}}{\gc@common}
\expandnext{\DeclareDocumentCommand{\grenewcommand}  {s+mo+o+m}}{\gc@common}
\expandnext{\DeclareDocumentCommand{\gprovidecommand}{s+mo+o+m}}{\gc@common}

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

\newcommand{\sc@currentscope}{}

\DTLnewdb{sc@db}

\newcommand{\sc@db@assignlist}{      %
    \sc@db@col@index      =index,    %
    \sc@db@col@scope      =scope,    %
    \sc@db@col@command    =command,  %
    \sc@db@col@definition =definition%
}

\newcommand{\sc@defit}{%
    \expandnexttwo\LetLtxMacro{\sc@db@col@command}{\sc@db@col@definition}%
}

\newcounter{sc@count}

\def\sc@common{%
    \addtocounter{sc@count}{1}%
    \DTLnewrow{sc@db}%
    \def\sc@def{\csname sc@def@\thesc@count\endcsname}%
    \dtlexpandnewvalue%
    \DTLnewdbentry{sc@db}{index}     {\thesc@count}%
    \DTLnewdbentry{sc@db}{scope}     {\sc@currentscope}%
    \DTLnewdbentry{sc@db}{command}   {\noexpand##2}%
    \DTLnewdbentry{sc@db}{definition}{\sc@def}%
    \begingroup\edef\x{\endgroup%
        \noexpand\gnewcommand%
        \IfBooleanT{##1}{*}%
        {\sc@def}%
        \IfNoValueF{##3}{[##3]}%
        \IfNoValueF{##4}{[\unexpanded{##4}]}%
        {\unexpanded{##5}}%
    }\x%
    \expandnext{\DTLassign{sc@db}{\thesc@count}}{\sc@db@assignlist}%
}

% TODO: Yeah, most of these do the same thing now. Fix their behavior.
\expandnext{\DeclareDocumentCommand{\sensurecommand}{s+mo+o+m}}%
    {\sc@common\sc@defit}
\expandnext{\DeclareDocumentCommand{\snewcommand}{s+mo+o+m}}%
    {\sc@common%
    \expandnext\newcommand{\sc@db@col@command}{}%
    \sc@defit}
\expandnext{\DeclareDocumentCommand{\srenewcommand}{s+mo+o+m}}%
    {\sc@common\sc@defit}
\expandnext{\DeclareDocumentCommand{\sprovidecommand}{s+mo+o+m}}%
    {\sc@common\sc@defit}

\newcommand{\sc@forall}[2][\boolean{true}]{%
    \expandnext{\DTLforeach[#1]{sc@db}}{\sc@db@assignlist}{#2}%
}

\newenvironment{scope}[1]{%
    \renewcommand{\sc@currentscope}{#1}%
    \sc@forall[\DTLiseq{\sc@db@col@scope}{\sc@currentscope}]{\sc@defit}%
}{}

\newcommand{\from}[2]{%
    {% explicit local scope
    \sc@forall[\DTLiseq{\sc@db@col@scope}{#1}]{\sc@defit}%
    #2%
    }%
}

\newcommand{\scshow}[1][\boolean{true}]{%
    \begin{tabular}{|r|l|l|l|}%
        \hline%
        & \bfseries scope & \bfseries command & \bfseries definition%
        \sc@forall[#1]{%
            \\\hline        %
            \sc@db@col@index                                     &%
            \ttfamily\expandnext\detokenize{\sc@db@col@scope}    &%
            \ttfamily\expandnext\detokenize{\sc@db@col@command}  &%
            \ttfamily\expandnext\detokenize{\sc@db@col@definition}%
        }%
        \\\hline%
    \end{tabular}%
}

\makeatother

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\begin{document}                                                               %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

\let\bar\relax

\begin{scope}{main}
    \snewcommand*{\foo}[1]{foo(#1)}
    \snewcommand*{\bar}{bar}
    \foo{\bar}   % foo(bar)
\end{scope}

\begin{scope}{hidden}
    \snewcommand{\secret}[1]{(secret #1)}
\end{scope}

\begin{scope}{main}
    \foo{\bar\bar}   % foo(barbar)

    \from{hidden}{\secret{discovered}}   % (secret discovered)
\end{scope}

\scshow   % <prints handy table showing the database>

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\end{document}                                                                 %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

当然不完整。但它解决了我最初提出的问题。试试吧!(请注意,它需要 的​​ v2.11 版本datatool。)欢迎提出意见!

相关内容