LaTeX 中的类/对象(OOP)

LaTeX 中的类/对象(OOP)

我想使用常规 LaTeX 命令创建类/对象来表示成员变量和成员函数。我想出了以下方法:

\documentclass{article}

\usepackage{expl3}
\usepackage{xfp}
\usepackage{tikz}

\ExplSyntaxOn
\NewDocumentCommand\NewDocumentCommandName{}{\exp_args:Nc \NewDocumentCommand}
\tl_new:N \l__object_path_tl
\NewDocumentCommand\NewObject{m m}{
    \tl_set:Nn \l__object_path_tl {\cs_to_str:N #1}
    \NewDocumentCommand#1{m}{\use:c {\cs_to_str:N #1 \cs_to_str:N ##1}} % TODO Use \l__object_path_tl for \cs_to_str:N #1
    #2
}
\NewDocumentCommand\NewObjectCommand{m m m}{
    \NewDocumentCommandName{\l__object_path_tl \cs_to_str:N #1}{#2}{#3}
}
\ExplSyntaxOff

\NewObject{\obja}{
    \NewObjectCommand{\var}{}{var obj~A}
}
\NewObject{\objb}{
    \NewObjectCommand{\var}{}{var obj~B}
    \NewObjectCommand{\func}{m O{}}{func obj~B with #1, #2, \obja\var\ and \objb\var} % TODO Allow to use \this as an alias for \objb
    \NewObjectCommand{\ref}{}{\obja}
}
\NewObject{\objc}{
    \NewObjectCommand{\vara}{}{1}
    \NewObjectCommand{\varb}{}{10}
    \NewObjectCommand{\varc}{}{\fpeval{100 + \objc\vara + \objc\varb}} % TODO Allow to use \this as an alias for \objc
}

\begin{document}
\noindent
document:\\
\obja\var\\
\objb\var\\
\objb\func{marg}[oarg]\\
\objb\ref\var\\
xfp:\\
\objc\varc\\
\fpeval{100 + \objc\vara + \objc\varb}\\
tikz/pgf:\\
\pgfmathsetmacro{\varpgf}{100 + \objc\vara + \objc\varb}\varpgf\\ % TODO error
\tikz \draw (0, 0) -- (\objc\vara + 1, 0); % TODO error
\end{document}

对象由对象命令组成。对象命令可用于同一对象的其他对象命令,也可用于其他对象的对象命令。对于第一种情况,我想将其用作\this当前对象的别名。是否可以定义一个临时命令,如果在对象命令的定义中使用,该命令将直接展开(而不展开其他命令)?如果我想使用自动展开的临时\this命令,则需要进行类似的展开。\l__object_path_tl\cs_to_str:N #1

对于对象和对象命令的定义,我使用非可扩展命令。因此,对象命令不能直接在 TikZ/PGF 命令中使用。然而,在 xfp 命令中可以使用非可扩展命令。是否也可以在 TikZ/PGF 命令中直接使用非可扩展命令?

有关的:

参考:

答案1

在论证中使用事情顺利完成的要求时,\setpgfmath暗示事情也应该在一切仅通过扩展来完成/在扩展阶段一切已经完成的情况下顺利完成。

因此,让每个对象函数定义一个临时宏来表示其所属对象的名称并不是一个选择。

在进一步处理来自第二个参数的标记之前,如何在其第二个参数中用对象的名称\NewObject替换标记的每个实例?\this#1

当然,这种方法\this不能被掩盖,例如,as\csname this\endcsname或 as \foobarafter \let\foobar=\this。此外,替换仅绑定到\this作为\NewObject第二个参数组成部分的 -token。

因此

\def\macrodefinitionhidesthis{Something with \this\var}
\let\foobar=\this
\NewObject{MyNewObject}{%
    \NewObjectCommand{\var}{}{My Object's var}%
    \NewObjectCommand{\whatsoever}{}{%
      ...
      \macrodefinitionhidesthis
      ... 
      \csname this\endcsname\var
      ... 
      \foobar\var
    }%
}

不会成功。

如果您仍然对“\this在定义时按对象名称替换”路线感兴趣:

乍一看\tl_replace_all:Nnn似乎很有希望。
但事实并非如此,因为替换不是在括号组内完成的。:-)

也许可以通过软件包 l3regex这是 expl3 的一部分,因此在界面3.pdf。但我还没有深入探讨这一点。

我可以即兴地提供一个简单的尾部递归可扩展例程

\ReplaceThis{⟨\this-replacement⟩}%
            {⟨tokens where the token \this shall be replaced by ⟨\this-replacement⟩}%

替换也在大括号组内部进行。


由于\romannumeral扩展,例程确实在两个扩展步骤/两次“命中”之后提供了结果\expandafter


该例程的要点是:

该例程启动尾递归循环\UD@ThisReplaceloop

\UD@ThisReplaceloop是一个宏,它一次又一次地调用自身,并且其参数在每次迭代后都被修改,直到完成所有的替换。

尾部递归\UD@ThisReplaceloop宏处理三个参数:

  • 一个参数 ⟨\this-replacement⟩——该参数包含应替换令牌的令牌\this
  • 另一个持有的论点是。⟨tokens where the token \this shall be replaced by ⟨\this-replacement⟩
  • 还有另一个论点⟨tokens forming the replacement-result gathered so far⟩

尾部递归\UD@ThisReplaceloop宏使 TeX “查看”参数的第一个标记:⟨tokens where the token \this shall be replaced by ⟨\this-replacement⟩

如果该参数为空,则工作已完成,因此⟨tokens forming the replacement-result gathered so far⟩已交付。

如果该参数的第一个标记是空格标记,则该标记不能作为未分隔的参数处理。相反,使用 TeX

  • 通过一个吞噬空格分隔参数的宏来删除它,然后
  • ⟨tokens forming the replacement-result gathered so far⟩在and后面添加一个空格
  • 再次循环。

如果该参数的第一个标记不是空格标记,则可以将其作为无分隔符参数进行处理。

如果该参数的第一个标记不是花括号,则让 TeX

  • 从该参数中提取/删除可以作为无界参数处理的“事物”,并
  • 如果该事物不等于令牌,则\this将该事物附加到⟨tokens forming the replacement-result gathered so far⟩
  • 如果事情确实等于令牌\this附加⟨\this-replacement⟩⟨tokens forming the replacement-result gathered so far⟩
  • 再次循环。

如果该参数的第一个标记是花括号,则让 TeX

  • 从该参数中提取/删除可以作为无界参数处理的“事物”,并
  • ⟨tokens forming the replacement-result gathered so far⟩附加到将替换例程应用于该事物的结果中,嵌套在一对花括号中
  • 再次循环。

该例程的缺点是:

作为副作用,任何匹配的 catcode 1(开始组)和 2(结束组)的显式字符标记对都会被匹配的显式字符标记对和 替换。{1}2

在正常情况下,{是类别代码 1 的唯一字符,并且}是类别代码 2 的唯一字符。
因此,在正常情况下,除了 之外,您不会获得类别代码 1 的明确字符标记, 并且除了 之外,您不会获得类别代码 2 的明确字符标记 。{1}2

所以在正常情况下这种副作用并不重要。

但它可能会在特殊情况下对您造成影响,即故意创建catcode 1 以外的显式字符标记 和/或 catcode 2 以外的显式字符标记 ,例如通过-assignments,例如通过/ -trickery,用于处理由 catcode 1 以外的显式字符标记分隔的参数的情况。处理此类参数的宏可以通过 -notation 定义;这是 TeXbook 中一些危险弯曲段落中描述的比较晦涩的东西之一。:-){1}2\catcode\uppercase\lowercase{1#⟨character of category code 1⟩


乍一看,所有“\romannumeral在交换参数之前驱动扩展”的诡计可能会让初学者感到困惑。如果是这样,那么不要害怕。只要你对扩展的概念稍微熟悉一点,一切都会显得相当简单。:-)

-expansion的要点\romannumeral是:

在收集要转换的数字时,\romannumeral会触发扩展事物,直到很明显收集了属于该数字的所有标记/数字(或需要引发错误消息)。

如果收集到的用于转换的数字不是正数,则\romannumeral-conversion 不会提供任何 token。组成该数字的 token 会被默默吞掉。

因此,只要确保最终该工作结果的第一部分是形成非正数的标记序列,您就可以使用它\romannumeral来触发大量扩展和宏参数交换工作。该序​​列将被删除。形成该工作结果其余部分的标记将照常处理。


很抱歉我还没有找到时间重新实现\ReplaceThisexpl3 的子组件。:-)

\errorcontextlines=10000
\makeatletter
%%///////// Code for \ReplaceThis /////////////////////////////////////////////
%% Syntax:
%% -------
%%
%% \ReplaceThis{<\this-replacement>}%
%%             {<tokens where \this shall be replaced by <\this-replacement>>}%
%%
%% The result is delivered after two expansion-steps/by two "hits"
%% with \expandafter.
%%
%% As a side-effect any matching pair of explicit character tokens 
%% of category code 1 and 2 is replaced by a matching pair of
%% explicit character-tokens {_1 and }_2.
%%
%%=============================================================================
%% PARAPHERNALIA:
%% \UD@firstoftwo, \UD@secondoftwo, \UD@PassFirstToSecond, \UD@Exchange,
%% \UD@removespace, \UD@stopromannumeral, \UD@CheckWhetherNull,
%% \UD@CheckWhetherBrace, \UD@CheckWhetherLeadingExplicitSpace,
%% \UD@ExtractFirstArg
%%=============================================================================
\newcommand\UD@firstoftwo[2]{#1}%
\newcommand\UD@secondoftwo[2]{#2}%
\newcommand\UD@PassFirstToSecond[2]{#2{#1}}%
\newcommand\UD@Exchange[2]{#2#1}%
\@ifdefinable\UD@removespace{\UD@Exchange{ }{\def\UD@removespace}{}}%
\@ifdefinable\UD@stopromannumeral{\chardef\UD@stopromannumeral=`\^^00}%
%%-----------------------------------------------------------------------------
%% Check whether argument is empty:
%%.............................................................................
%% \UD@CheckWhetherNull{<Argument which is to be checked>}%
%%                     {<Tokens to be delivered in case that argument
%%                       which is to be checked is empty>}%
%%                     {<Tokens to be delivered in case that argument
%%                       which is to be checked is not empty>}%
%%
%% The gist of this macro comes from Robert R. Schneck's \ifempty-macro:
%% <https://groups.google.com/forum/#!original/comp.text.tex/kuOEIQIrElc/lUg37FmhA74J>
\newcommand\UD@CheckWhetherNull[1]{%
  \romannumeral\expandafter\UD@secondoftwo\string{\expandafter
  \UD@secondoftwo\expandafter{\expandafter{\string#1}\expandafter
  \UD@secondoftwo\string}\expandafter\UD@firstoftwo\expandafter{\expandafter
  \UD@secondoftwo\string}\expandafter\UD@stopromannumeral\UD@secondoftwo}{%
  \expandafter\UD@stopromannumeral\UD@firstoftwo}%
}%
%%-----------------------------------------------------------------------------
%% Check whether argument is blank (empty or only spaces):
%%-----------------------------------------------------------------------------
%% -- Take advantage of the fact that TeX discards space tokens when
%%    "fetching" _un_delimited arguments: --
%% \UD@CheckWhetherBlank{<Argument which is to be checked>}%
%%                      {<Tokens to be delivered in case that
%%                        argument which is to be checked is blank>}%
%%                      {<Tokens to be delivered in case that argument
%%                        which is to be checked is not blank>}%
\newcommand\UD@CheckWhetherBlank[1]{%
  \romannumeral\expandafter\expandafter\expandafter\UD@secondoftwo
  \expandafter\UD@CheckWhetherNull\expandafter{\UD@firstoftwo#1{}{}}%
}%
%%-----------------------------------------------------------------------------
%% Check whether argument's first token is a catcode-1-character
%%.............................................................................
%% \UD@CheckWhetherBrace{<Argument which is to be checked>}%
%%                      {<Tokens to be delivered in case that argument
%%                        which is to be checked has a leading
%%                        explicit catcode-1-character-token>}%
%%                      {<Tokens to be delivered in case that argument
%%                        which is to be checked does not have a
%%                        leading explicit catcode-1-character-token>}%
\newcommand\UD@CheckWhetherBrace[1]{%
  \romannumeral\expandafter\UD@secondoftwo\expandafter{\expandafter{%
  \string#1.}\expandafter\UD@firstoftwo\expandafter{\expandafter
  \UD@secondoftwo\string}\expandafter\UD@stopromannumeral\UD@firstoftwo}{%
  \expandafter\UD@stopromannumeral\UD@secondoftwo}%
}%
%%-----------------------------------------------------------------------------
%% Check whether brace-balanced argument starts with a space-token
%%.............................................................................
%% \UD@CheckWhetherLeadingExplicitSpace{<Argument which is to be checked>}%
%%                                     {<Tokens to be delivered in case <argument
%%                                       which is to be checked> does have a
%%                                       leading explicit space-token>}%
%%                                     {<Tokens to be delivered in case <argument
%%                                       which is to be checked> does not have a
%%                                       a leading explicit space-token>}%
\newcommand\UD@CheckWhetherLeadingExplicitSpace[1]{%
  \romannumeral\UD@CheckWhetherNull{#1}%
  {\expandafter\UD@stopromannumeral\UD@secondoftwo}%
  {%
    % Let's nest things into \UD@firstoftwo{...}{} to make sure they are nested in braces
    % and thus do not disturb when the test is carried out within \halign/\valign/
    % tabular-environment:
    \expandafter\UD@firstoftwo\expandafter{%
      \expandafter\expandafter\expandafter\UD@stopromannumeral
      \romannumeral\expandafter\UD@secondoftwo
      \string{\UD@CheckWhetherLeadingExplicitSpaceB.#1 }{}%
    }{}%
  }%
}%
\@ifdefinable\UD@CheckWhetherLeadingExplicitSpaceB{%
  \long\def\UD@CheckWhetherLeadingExplicitSpaceB#1 {%
    \expandafter\UD@CheckWhetherNull\expandafter{\UD@firstoftwo{}#1}%
    {\UD@Exchange{\UD@firstoftwo}}{\UD@Exchange{\UD@secondoftwo}}%
    {\expandafter\expandafter\expandafter\UD@stopromannumeral
     \expandafter\expandafter\expandafter}%
     \expandafter\UD@secondoftwo\expandafter{\string}%
  }%
}%
%%-----------------------------------------------------------------------------
%% Extract first inner undelimited argument:
%%.............................................................................
%%   \UD@ExtractFirstArg{ABCDE} yields  {A}
%%
%%   \UD@ExtractFirstArg{{AB}CDE} yields  {{AB}}
%%
%% Due to \romannumeral-expansion the result is delivered after two 
%% expansion-steps/after "hitting" \UD@ExtractFirstArg with \expandafter
%% twice.
%%
%% \UD@ExtractFirstArg's argument must not be blank.
%% This case can be cranked out via \UD@CheckWhetherBlank before calling
%% \UD@ExtractFirstArg.
%%
%% Use frozen-\relax as delimiter for speeding things up.
%% Frozen-\relax is chosen because David Carlisle pointed out in
%% <https://tex.stackexchange.com/a/578877>
%% that frozen-\relax cannot be (re)defined in terms of \outer and cannot be
%% affected by \uppercase/\lowercase.
%%
%% \UD@ExtractFirstArg's argument may contain frozen-\relax:
%% The only effect is that internally more iterations are needed for
%% obtaining the result.
%%.............................................................................
\@ifdefinable\UD@RemoveTillFrozenrelax{%
  \expandafter\expandafter\expandafter\UD@Exchange
  \expandafter\expandafter\expandafter{%
  \expandafter\expandafter\ifnum0=0\fi}%
  {\long\def\UD@RemoveTillFrozenrelax#1#2}{{#1}}%
}%
\expandafter\UD@PassFirstToSecond\expandafter{%
  \romannumeral\expandafter
  \UD@PassFirstToSecond\expandafter{\romannumeral
    \expandafter\expandafter\expandafter\UD@Exchange
    \expandafter\expandafter\expandafter{%
    \expandafter\expandafter\ifnum0=0\fi}{\UD@stopromannumeral#1}%
  }{%
    \UD@stopromannumeral\romannumeral\UD@ExtractFirstArgLoop
  }%
}{%
  \newcommand\UD@ExtractFirstArg[1]%
}%
\newcommand\UD@ExtractFirstArgLoop[1]{%
  \expandafter\UD@CheckWhetherNull\expandafter{\UD@firstoftwo{}#1}%
  {\UD@stopromannumeral#1}%
  {\expandafter\UD@ExtractFirstArgLoop\expandafter{\UD@RemoveTillFrozenrelax#1}}%
}%
%====================================================================
\@ifdefinable\UD@ThisInstance{\long\def\UD@ThisInstance#1\this#2#3!{#2}}%
\newcommand\UD@ReplaceThisInstance[2]{%
  % #1 a single token to examine
  % #2 \this-replacement
  \UD@ThisInstance#1{\UD@stopromannumeral#2}\this{\UD@stopromannumeral#1}!%
}%
\newcommand\UD@ThisReplaceloop[3]{%
  % #1 replacement for \this
  % #2 tokens forming the result gathered so far
  % #3 remaining token list to process
  \UD@CheckWhetherNull{#3}{\UD@stopromannumeral#2}{%
    \expandafter\UD@Exchange\expandafter{%
      \romannumeral
      \UD@CheckWhetherLeadingExplicitSpace{#3}{%
        \expandafter\UD@PassFirstToSecond
        \expandafter{\UD@removespace#3}{\UD@stopromannumeral{#2 }}%
      }{%
        \expandafter\UD@PassFirstToSecond\expandafter{\UD@firstoftwo{}#3}{%
          \expandafter\expandafter\expandafter\UD@stopromannumeral
          \expandafter\expandafter\expandafter{%
            \expandafter\UD@Exchange\expandafter{%
              \romannumeral
              \UD@CheckWhetherBrace{#3}{%
                \expandafter\UD@stopromannumeral\expandafter{\romannumeral
                  \expandafter\expandafter\expandafter\UD@PassFirstToSecond
                  \UD@ExtractFirstArg{#3}{\UD@ThisReplaceloop{#1}{}}%
                }%
              }{%
                \expandafter\expandafter\expandafter\UD@ReplaceThisInstance
                \UD@ExtractFirstArg{#3}{#1}%
              }%
            }%
            {#2}%
          }%
        }%
      }%
    }{\UD@ThisReplaceloop{#1}}%
  }%
}%
\newcommand\ReplaceThis[2]{%
  % #1 \this-replacement
  % #2 tokens where to \replace \this by \this-replacement
  \romannumeral\UD@ThisReplaceloop{#1}{}{#2}%
}%
%%///////// End of code for \ReplaceThis //////////////////////////////////////
\makeatother

\documentclass{article}

\usepackage{expl3}
\usepackage{xfp}
\usepackage{tikz}

\ExplSyntaxOn
\NewDocumentCommand\NewDocumentCommandName{}{\exp_args:Nc \NewExpandableDocumentCommand}
\tl_new:N \l__object_path_tl
\NewDocumentCommand\NewObject{ m }{
    \tl_set:Nn \l__object_path_tl {\cs_to_str:N #1}
    \newcommand#1[1]{\use:c {\cs_to_str:N #1 \cs_to_str:N ##1}} % I don't recommend using the scratch-variable
                                                                % \l__object_path_tl for \cs_to_str:N #1
                                                                % insise the definition of \<object> for the following 
                                                                % reason:
                                                                % The invariant condition for that scratch-variable is:
                                                                % At the time of _defining_ an <object> it denotes the
                                                                % name of the <object>.
                                                                % But at the time of carrying out an <object>'s 
                                                                % command \<object> that variable may have whatsoever
                                                                % value not denoting the name of the <object> 
                                                                % in question.
    \ReplaceThis{#1}%
}
\NewDocumentCommand\NewObjectCommand{ m }{
    \NewDocumentCommandName{\l__object_path_tl \cs_to_str:N #1}
}
\ExplSyntaxOff

% This is not in \ExplSyntax any more, thus you should get used to taking care of the
% \endlinechar-mecnanism yielding space-tokens at end of lines in case lines end with
% something that switches TeX's reading apparatus to state M(middle of line), e.g., 
% explicit non-space-character-tokens, control-symbol-tokens other than control-space, ...
% In horizontal mode these space-tokens might yield undesired horizontal glue.
%
% You are in luck that all your \NewObject-commands are carried out in the
% preamble where TeX still is in vertical mode where horizontal glue doesn't matter.
% 
% But not caring about the \endlinechar-mechanism might bite you when defining
% objects while TeX is in horizontal-mode.


\NewObject{\obja}{%%%%
    \NewObjectCommand{\var}{}{var obj~A}%%%%
}%%%%
\NewObject{\objb}{%%%%
    \NewObjectCommand{\var}{}{var obj~B}%%%%
    \NewObjectCommand{\func}{O{}m}{func obj~B with #1, #2, \obja\var\ and \this\var}%%%%
    \NewObjectCommand{\refobja}{}{\obja}%%%%
    \NewObjectCommand{\ref}{m}{\csname#1\endcsname}%%%%
}%%%%
\NewObject{\objc}{%%%%
    \NewObjectCommand{\vara}{}{1}%%%%
    \NewObjectCommand{\varb}{}{10}%%%%
    \NewObjectCommand{\varc}{}{\fpeval{100 + \this\vara + \this\varb}}%%%%
}%%%%

\begin{document}
\noindent
document:\\
\verb|\obja\var|: \obja\var\\
\verb|\objb\var|: \objb\var\\
\verb|\objb\func[oarg]{marg}|: \objb\func[oarg]{marg}\\ % With \NewExpandableDocumntCommand 
                                                        % the last argument cannot be an optional one.
                                                        % See the xparse-manual.
\verb|\objb\refobja\var|: \objb\refobja\var\\
\verb|\objb\ref{obja}\var|: \objb\ref{obja}\var\\
\verb|\objb\ref{objb}\var|: \objb\ref{objb}\var\\
\verb|\objb\ref{objc}\varc|: \objb\ref{objc}\varc\\
xfp:\\
\verb|\objc\varc|: \objc\varc\\
\verb|\fpeval{100 + \objc\vara + \objc\varb}|: \fpeval{100 + \objc\vara + \objc\varb}\\
tikz/pgf:\\
\verb|\pgfmathsetmacro{\varpgf}{100 + \objc\vara + \objc\varb}\varpgf|: \pgfmathsetmacro{\varpgf}{100 + \objc\vara + \objc\varb}\varpgf\\ 
\verb|\tikz \draw (0, 0) -- (\objc\vara + 1, 0); |: \tikz \draw (0, 0) -- (\objc\vara + 1, 0); 
\end{document}

在此处输入图片描述

答案2

我采纳了@Ulrich Diez 在他的回答\this使用 l3regex在对象定义的代码中替换#2对象本身的命令,#1如中所述interface3手册

对于使用 tikz/pgf 和 siunitx 的对象命令(我将其添加为附加用例),我使用了可扩展文档命令(如@Ulrich Diez 所做的那样),并接受了xparse 手册

\documentclass{article}
%
\usepackage{expl3}
\usepackage{xfp}
\usepackage{tikz}
\usepackage{siunitx}
%
\ExplSyntaxOn
\NewDocumentCommand\NewExpandableDocumentCommandName{}{
    \exp_args:Nc \NewExpandableDocumentCommand
}
\tl_new:N \l__object_path_tl
\NewDocumentCommand\NewObject{m m}{
    \tl_set:Nn \l__object_path_tl {\cs_to_str:N #1}
    
    % Use expandable document command as needed for use with tikz/pgf and siunitx
    % TODO Use \l__object_path_tl for \cs_to_str:N #1 (must be expandend therefor)
    \NewExpandableDocumentCommand#1{m}{\use:c {\cs_to_str:N #1 \cs_to_str:N ##1}}
    
    % Replace \this used in #2 by #1 (see interface3 manual)
    \tl_set:Nn \l__object_command_tl {#1}
    \tl_set:Nn \l__object_code_tl {#2}
    \regex_replace_all:nnN {\c{this}} {\u{\l__object_command_tl}} \l__object_code_tl
    \l__object_code_tl
}
\NewDocumentCommand\NewObjectCommand{m m m}{
    % Use expandable document command as needed for use with tikz/pgf and siunitx
    % TODO Remove arguments #2 and #3 as not needed because they are subsequent arguments
    % that get processed by \NewExpandableDocumentCommandName anyway
    \NewExpandableDocumentCommandName{\l__object_path_tl \cs_to_str:N #1}{#2}{#3}
}
\ExplSyntaxOff
%
\NewObject{\obja}{%
    \NewObjectCommand{\var}{}{var obj~A}%
}%
\NewObject{\objb}{%
    \NewObjectCommand{\var}{}{var obj~B}%
    % Ensure that last argument is a mandatory argument because object commands are
    % expandable document commands (see restrictions described in xparse manual)
    \NewObjectCommand{\func}{O{} m}{func obj~B with #1, #2, \obja\var\ and \this\var}%
    \NewObjectCommand{\ref}{}{\obja}%
}%
\NewObject{\objc}{%
    \NewObjectCommand{\vara}{}{1}%
    \NewObjectCommand{\varb}{}{10}%
    \NewObjectCommand{\varc}{}{\fpeval{100 + \this\vara + \this\varb}}%
    \NewObjectCommand{\unit}{}{\milli\meter}%
}%
%
\begin{document}
\noindent
document:\\
\obja\var\\
\objb\var\\
\objb\func[oarg]{marg}\\
\objb\ref\var\\
xfp:\\
\objc\varc\\
\fpeval{100 + \objc\vara + \objc\varb}\\
tikz/pgf:\\
\pgfmathsetmacro{\varpgf}{100 + \objc\vara + \objc\varb}\varpgf\\
\tikz \draw (0, 0) -- (\objc\vara + 1, 0);\\
siunitx:\\
\si{\objc\unit}%
\end{document}

目前为止一切正常,但我不确定用 l3regex 替换 是否是\this一种“黑客”解决方案。我还尝试在定义每个对象命令时将替换限制为其代码,但如果不扩展替换所需的临时标记列表,这似乎是不可能的。#2#1

相关内容