多次调用同一函数——动态变量的覆盖

多次调用同一函数——动态变量的覆盖

我想要 LaTeX 做:

"H(k,c,a,b)=L(a,b,c,d)*T(c,k)",tensor_H,tensor_L2,tensor_t1

从:

\contractFMS{GAB}{H,kacb}{L2,abij}{t1,ck}

我已经从一个函数开始,但是变量正在覆盖。

我的 MWE:

\documentclass[10pt,reqno,a4paper,twoside,french]{report}
\usepackage{ifthen}
\usepackage{xparse}
\usepackage{pdftexcmds}

%% Name of tenseur currently used
\def\NTenstensor{\texttt{tensor\_}}

\ExplSyntaxOn % To split
\NewDocumentCommand{\SND}{ >{\SplitArgument{2}{,}} m %
}{\pSND#1}%
\NewDocumentCommand{\pSND}{mmm}{%
        \ifthenelse{\equal{#1}{t1}}%
        {   \def\NOM{\NTenstensor#1}%
            \def\Tenseur{\Tsfo #2}%
            \def\Dimension{NaN} }%
        {}% 
        \ifthenelse{\equal{#1}{L2}}%
        {   \def\NOM{\NTenstensor#1}%
            \def\Tenseur{\Ldfo #2}%
            \def\Dimension{NaN} }%
        {}% 
        \ifthenelse{\equal{#1}{H}}%
        {   \def\NOM{\NTenstensor#1}%
            \def\Tenseur{\Hdfo #2}%
            \def\Dimension{NaN} }%
        {}% 
}%
\ExplSyntaxOff

%% To write
\newcommand{\contractFMS}[4]{%
    \ifthenelse{\equal{#1}{GAB}}{
        \SND{#2}%
        \def\NUn{\NOM} 
        \def\TUn{\Tenseur}  
\noindent First Call : \texttt{\TUn,\NUn,}\\
        \SND{#3}%
        \def\NDe{\NOM} 
        \def\TDe{\Tenseur}
\noindent Second Call : \texttt{\TUn,\NUn,}\\
        \SND{#4}%
        \def\NTr{\NOM} 
        \def\TTr{\Tenseur}
\noindent Third Call : \texttt{\TUn,\NUn,}\\        
\noindent           Result : \texttt{"\TUn=\TDe*\TTr",\NUn,\NDe,\NTr}
    }{}%
}%

\newcommand{\Tsfo}[2]{T(#1,#2)}
\newcommand{\Ldfo}[4]{L(#1,#2,#3,#4)}
\newcommand{\Hdfo}[4]{H(#1,#2,#3,#4)}

\begin{document}

\contractFMS{GAB}{H,kacb}{L2,abij}{t1,ck}

\end{document}

我测试了这些变化,\expandafter但没有成功。

答案1

你的评论

只是一点额外的帮助,我不想使用逗号作为分隔符,而只使用空格:\contractFMS{GAB}{H kacb}{L2 abij}{t1 ck},但\SplitArgument {2} {}不起作用......

导致我重写我的答案。

一些初步的评论:

在正常的 LaTeX 语法中,空格字符(),在 TeX 引擎的内部字符编码方案中代码点编号为 32(在传统 TeX 引擎中为 ASCII,在基于 LuaTeX 或 XeTeX 的 TeX 引擎中为 UTF8),当读取设备处于状态 M(行中)时,会被标记为正常空格标记。
在正常/非解释情况下,输入行末尾的所有空格字符都会被删除。然后在输入行的末尾附加一个返回字符(TeX 引擎内部字符编码方案中代码点编号为 13;\endlinechar-parameter 的值通常为 13)。正常情况下,返回字符的类别代码为 5(行尾)。
如果读取设备处于状态 S(跳过空格),则类别代码 5 的字符根本不会产生任何标记。
如果读取设备处于状态 N(新行),则类别代码 5 的字符都会产生标记,\par而不管 的含义如何\par
如果阅读器处于状态 M(行中),则类别代码 5 的字符会产生显式空格标记(类别代码 10(空格)和字符代码 32 的字符标记)。
这意味着:如果阅读器在到达 .tex 输入行的末尾时处于状态 M(行中),则显式空格标记将插入到标记流中与 .tex 源代码中行的末尾相对应的位置。您可以通过使用注释字符( )结束不以控制字标记结尾的输入行(此后阅读器将处于状态 S(跳过空格))来避免这种情况%。空格标记反过来在(受限)水平模式下产生水平粘连。波浪号(~)表示不间断空格。

在 expl3 语法中,情况有所不同:所有空白(空格字符等)都会被忽略。\endlinechar表示要忽略的空格字符,因此是空白,而不是返回字符。(因此,消除了从空格中滑入宏定义(在读取设备处于状态 m(行中)的位置/行尾)而产生不必要的水平粘连的风险。)正常的空格标记用波浪线表示。

如果您希望使用空格标记作为分隔符而不是逗号,则宏代码中的参数分隔符应该是空格标记。
在正常的 LaTeX 语法中,.tex 输入中的空格标记由空格字符表示。
在 expl3 语法中,空格标记由波浪号表示。

如果您希望可以写空格或逗号,那么在拆分之前只需检查是否存在逗号,如果存在逗号,则在逗号处拆分参数,如果没有逗号,则假定该参数可以在空格处拆分。

检查逗号是否存在可以按如下方式实现:在参数后附加一个逗号,然后让 LaTeX 吞噬第一个逗号之前的所有内容。如果没有逗号,则结果为空/根本没有标记。如果有逗号,则结果不为空/由第一个逗号和附加逗号后的标记之间的标记组成。即,结果的最后一个标记是附加的逗号。

检查逗号是否存在意味着需要检查宏参数是否为空。以下示例中\UD@CheckWhetherNull使用了。\UD@CheckWhetherNull解释如下我的答案针对这个问题“空标记列表的可扩展测试——方法、性能和稳健性”(在该答案中,宏被命名为\CheckWhetherEmpty。)

当我们处理分隔参数时,需要考虑周围的空格/需要删除周围的空格。

我建议使用xparse 的 >{\TrimSpaces}带有 expl3 语法的参数处理器。

我还建议只将那些东西放入\ifthenelse不总是相同的分支并嵌套调用,\ifthenelse以便后续\ifthenelse操作仅在需要时执行。

经过一些修改,您的代码将变成:

\documentclass[a4paper]{report}
\usepackage{ifthen}
\usepackage{xparse}

\makeatletter
%%.............................................................................
%% \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]{%
  \romannumeral0\expandafter\@secondoftwo\string{\expandafter
  \@secondoftwo\expandafter{\expandafter{\string#1}\expandafter
  \@secondoftwo\string}\expandafter\@firstoftwo\expandafter{\expandafter
  \@secondoftwo\string}\@firstoftwo\expandafter{} \@secondoftwo}%
  {\@firstoftwo\expandafter{} \@firstoftwo}%
}%    
\@ifdefinable\@gobbletocomma{\long\def\@gobbletocomma#1,{}}
\newcommand\CheckWhetherNoComma[1]{%
  \expandafter\UD@CheckWhetherNull\expandafter{\@gobbletocomma#1,}%
}%

\ExplSyntaxOn % To split
\NewDocumentCommand{\SplitInTwoArgsAtSpace}{ m >{\SplitArgument{1}{~}}m }{#1#2}
\NewDocumentCommand{\SplitInTwoArgsAtComma}{ m >{\SplitArgument{1}{,}}m }{#1#2}
\NewDocumentCommand{\SND}{ m }{
  \CheckWhetherNoComma{#1}{\SplitInTwoArgsAtSpace}{\SplitInTwoArgsAtComma}{\pSND}{#1}
}
\NewDocumentCommand{\pSND}{>{\TrimSpaces}m >{\TrimSpaces}m }{
  \ifthenelse{\equal{#1}{t1}}{\def\Tenseur{\Tsfo #2}\@firstofone}{
    \ifthenelse{\equal{#1}{L2}}{\def\Tenseur{\Ldfo #2}\@firstofone}{
      \ifthenelse{\equal{#1}{H}}{\def\Tenseur{\Hdfo #2}\@firstofone}{\@gobble}
    }
  }
  {
    % These are always the same unless none of the three cases is given:
    \def\NOM{\NTenstensor#1}
    %\def\Dimension{NaN}
  }
}
\NewDocumentCommand{\contractFMS}{>{\TrimSpaces}m>{\TrimSpaces}m>{\TrimSpaces}m>{\TrimSpaces}m}{
  \ifthenelse{\equal{#1}{GAB}}{
    \SND{#2}
    \expandafter\def\expandafter\NUn\expandafter{\NOM}
    \expandafter\def\expandafter\TUn\expandafter{\Tenseur}
    \SND{#3}
    \expandafter\def\expandafter\NDe\expandafter{\NOM}
    \expandafter\def\expandafter\TDe\expandafter{\Tenseur}
    \SND{#4}
    \expandafter\def\expandafter\NTr\expandafter{\NOM}
    \expandafter\def\expandafter\TTr\expandafter{\Tenseur}
    \noindent First Call~:~\texttt{\TUn,\NUn,}\\
    \noindent Second Call~:~\texttt{\TDe,\NDe,}\\
    \noindent Third Call~:~\texttt{\TTr,\NTr,}\\
    \noindent Result~:~\texttt{"\TUn=\TDe*\TTr",\NUn,\NDe,\NTr}
  }{}
}
\ExplSyntaxOff
\makeatother

\newcommand{\Tsfo}[2]{T(#1,#2)}
\newcommand{\Ldfo}[4]{L(#1,#2,#3,#4)}
\newcommand{\Hdfo}[4]{H(#1,#2,#3,#4)}
%% Name of tensor currently used
\newcommand*\NTenstensor{tensor\_}

\begin{document}

\contractFMS{ GAB }{H kacb}{L2 , abij}{t1,ck}

\end{document}

在此处输入图片描述

请注意,此结果与您在问题中要求的结果不同:

"H(k,c,a,b)=L(a,b,c,d)*T(c,k)",tensor_H,tensor_L2,tensor_t1

如果您问题中的请求没有拼写错误,请说明更换/交换物品的具体规则。

\SND还请注意,使用/\pSND定义 scratch-macros的方法\NOM\Tenseur\Dimension(后者在您的示例中从未使用过)并在调用\SND拆分其中一个参数之后扩展它们,您必然会在 -component后面按特定顺序传递t1-、L2- 和-component 。HGAB


如果您不想受限于特定的顺序,唯一的要求是 -componentGAB始终是第一个参数,我建议不要定义临时宏,而是传递您希望最终定义的标记(\NUn/ \TUn\NDe/ \TDe\NTr/ \Ttr)和用于处理拆分组件的宏(\Tsfo\Ldfo, , ),这些宏需要进入宏定义,作为分叉机制本身的参数,在您的示例中,它是通过包\fHdo组成的。\ifthenelseifthen

顺便说一句:由于使用了 expl3-syntax,\tl_if_eq:nnTF所以可用,因此您可能不需要ifthen

如果在现实生活场景/用例中您有更多组件和更多情况需要区分,那么\tl_map_tokens:nn用于在未限定参数列表上进行迭代的 expl3 可能会成为您的朋友。

\expandafter除此之外,代码中没有太多内容:

\documentclass[a4paper]{report}
\usepackage{xparse}

\makeatletter
%%.............................................................................
%% \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]{%
  \romannumeral0\expandafter\@secondoftwo\string{\expandafter
  \@secondoftwo\expandafter{\expandafter{\string#1}\expandafter
  \@secondoftwo\string}\expandafter\@firstoftwo\expandafter{\expandafter
  \@secondoftwo\string}\@firstoftwo\expandafter{} \@secondoftwo}%
  {\@firstoftwo\expandafter{} \@firstoftwo}%
}%    
\@ifdefinable\@gobbletocomma{\long\def\@gobbletocomma#1,{}}
\newcommand\CheckWhetherNoComma[1]{%
  \expandafter\UD@CheckWhetherNull\expandafter{\@gobbletocomma#1,}%
}%

\ExplSyntaxOn % To split

% \tl_map_tokens:nn  and  \@@_map_tokens:nn  were added to expl3 in 2019-09-05.
% In case your expl3-release is not that recent, define these control sequences here.
% The following is just a copy of the code from l3tl.dtx, released 2020-02-14.
%
% \tl_map_tokens:nn {<list of undelimited arguments>}{<tokens to prepend>}
% iterates on the <list of undelimited arguments> and in each iteration
% prepends <tokens to prepend> to the current element of the list.
% I.e., <tokens to prepend>{<current element of the list>} gets executued,
% then either the next iteration is carried out or iteration is terminated.
\if_cs_exist:N \tl_map_tokens:nn \else
  \cs_new:Npn \tl_map_tokens:nn #1#2
    {
      \@@_map_tokens:nn {#2} #1
      \q_recursion_tail
      \prg_break_point:Nn \tl_map_break: { }
    }
\fi
\if_cs_exist:N \@@_map_tokens:nn \else
  \cs_new:Npn \@@_map_tokens:nn #1#2
    {
      \quark_if_recursion_tail_break:nN {#2} \tl_map_break:
      \use:n {#1} {#2}
      \@@_map_tokens:nn {#1}
    }
\fi

\NewDocumentCommand{\SplitInTwoArgsAtSpace}{ m >{\SplitArgument{1}{~}}m }{#1#2}
\NewDocumentCommand{\SplitInTwoArgsAtComma}{ m >{\SplitArgument{1}{,}}m }{#1#2}
\NewDocumentCommand{\SND}{ m }{
  \CheckWhetherNoComma{#1}{\SplitInTwoArgsAtSpace}{\SplitInTwoArgsAtComma}{\pSND}{#1}
}
\NewDocumentCommand{\pSND}{>{\TrimSpaces}m >{\TrimSpaces}m }{
  % #1 = first component without surrounding spaces
  % #2 = second component without surrounding spaces
  \tl_map_tokens:nn{
     % A List of 5-tuples, each tuple of pattern:
     %{  
     %   {<first component equals>}
     %   {<macro to define from first component in case first component equals>}
     %   {<prefix/preprocessor in macro for first component>}
     %   {<macro to define from second component in case first component equals>}
     %   {<prefix/preprocessor in macro for second component>}
     %} 
     {{t1}{\NTr}{\NTenstensor}{\TTr}{\Tsfo}}
     {{L2}{\NDe}{\NTenstensor}{\TDe}{\Ldfo}}
     {{H}{\NUn}{\NTenstensor}{\TUn}{\Hdfo}}
  }{\pSNDDefine{#1}{#2}}
}
\NewDocumentCommand{\pSNDDefine}{mmm}{\pSNDDefinUnwrapped{#1}{#2}#3}
\NewDocumentCommand{\pSNDDefinUnwrapped}{mmmmmmm}{%
   %#1 - first component
   %#2 - second component
   %#3 - first component equals
   %#4 - macro to define from first component in case first component equals
   %#5 - prefix/preprocessor in macro for first component
   %#6 - macro to define from second component in case first component equals
   %#7 - prefix/preprocessor in macro for second component
   \tl_if_eq:nnTF{#1}{#3}{\def#4{#5#1}\def#6{#7#2}}{}
   %\ifthenelse{\equal{#1}{#3}}{\def#4{#5#1}\def#6{#7#2}}{}
}%
\NewDocumentCommand{\contractFMS}{>{\TrimSpaces}m>{\TrimSpaces}m>{\TrimSpaces}m>{\TrimSpaces}m}{
  \tl_if_eq:nnTF{#1}{GAB}{
    \tl_map_tokens:nn{{#2}{#3}{#4}}{\SND}
    \noindent First Call~:~\texttt{\TUn,\NUn,}\\
    \noindent Second Call~:~\texttt{\TDe,\NDe,}\\
    \noindent Third Call~:~\texttt{\TTr,\NTr,}\\
    \noindent Result~:~\texttt{"\TUn=\TDe*\TTr",\NUn,\NDe,\NTr}
   }{}
}
\ExplSyntaxOff
\makeatother

\newcommand{\Tsfo}[2]{T(#1,#2)}
\newcommand{\Ldfo}[4]{L(#1,#2,#3,#4)}
\newcommand{\Hdfo}[4]{H(#1,#2,#3,#4)}

%% Name of tensor currently used
\newcommand*\NTenstensor{tensor\_}

\begin{document}

\newsavebox\prettyprintbox

\begin{lrbox}{\prettyprintbox}%
\verb|\contractFMS{ GAB }{H kacb}{L2 , abij}{t1,ck}|%
\end{lrbox}%
\begingroup\centering\framebox{\usebox{\prettyprintbox} yields:}\smallskip\par\endgroup
\contractFMS{ GAB }{H kacb}{L2 , abij}{t1,ck}

\noindent\hrulefill\null\vspace{\ht\strutbox}

\begin{lrbox}{\prettyprintbox}%
\verb|\contractFMS{ GAB }{H kacb}{t1 ck}{L2 , abij}|%
\end{lrbox}%
\begingroup\centering\framebox{\usebox{\prettyprintbox} yields:}\smallskip\par\endgroup
\contractFMS{ GAB }{H kacb}{t1 ck}{L2 , abij}

\noindent\hrulefill\null\vspace{\ht\strutbox}

\begin{lrbox}{\prettyprintbox}%
\verb|\contractFMS{ GAB }{t1,ck}{H kacb }{ L2  abij }|%
\end{lrbox}%
\begingroup\centering\framebox{\usebox{\prettyprintbox} yields:}\smallskip\par\endgroup
\contractFMS{ GAB }{t1,ck}{H kacb }{ L2  abij }

\noindent\hrulefill\null\vspace{\ht\strutbox}

\begin{lrbox}{\prettyprintbox}%
\verb|\contractFMS{GAB}{t1 ,ck}{L2 , abij}{H kacb}|%
\end{lrbox}%
\begingroup\centering\framebox{\usebox{\prettyprintbox} yields:}\smallskip\par\endgroup
\contractFMS{GAB}{t1 ,ck}{L2 , abij}{H kacb}

\noindent\hrulefill\null\vspace{\ht\strutbox}

\begin{lrbox}{\prettyprintbox}%
\verb|\contractFMS{ GAB}{L2 , abij}{ t1, ck}{H kacb}|%
\end{lrbox}%
\begingroup\centering\framebox{\usebox{\prettyprintbox} yields:}\smallskip\par\endgroup
\contractFMS{ GAB}{L2 , abij}{ t1, ck}{H kacb}

\noindent\hrulefill\null\vspace{\ht\strutbox}

\begin{lrbox}{\prettyprintbox}%
\verb|\contractFMS{GAB }{ L2  abij }{ H kacb }{ t1 ck  }|%
\end{lrbox}%
\begingroup\centering\framebox{\usebox{\prettyprintbox} yields:}\smallskip\par\endgroup
\contractFMS{ GAB }{ L2  abij }{ H kacb }{ t1 ck  }

\end{document}

在此处输入图片描述

相关内容