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