\def vs xparse \NewDocumentCommand 带有分隔参数

\def vs xparse \NewDocumentCommand 带有分隔参数

根据我之前的问题xparse我注意到(恕我直言)用普通的 TeX来制作带有分隔参数的宏并不容易\def。据我所知,LaTeX 方式 - \newcommand- 根本没有定义这种类型参数的机制。应该说有xparse一种创建此类参数的机制,但仍然存在一些问题。

这是另一个例子,其中用 创建特殊分隔的参数非常容易\def,但使用机制来完成可能需要一些技巧xparse

\def\braces(#1#2|#3#4){
    \ensuremath{
        \int \frac{\chi_#1(1)\chi_#2(1)\chi_#3(2)\chi_#4(2)}{r_{12}} dV_1 dV_2
    }
}

所以我想我会再次向社区询问如何使用xparse(可能与 LaTeX3 结合使用或仅使用 LaTeX3)来做到这一点的建议。我希望这对我以及阅读此问题的人都有启发。此外,正确且现代的 LaTeX 定义命令的方式不是,\def甚至不再是\newcommand使用,而是使用xparse

答案1

我猜你只想要那个\chi部分。

\documentclass{article}
\usepackage{amsmath}

\NewDocumentCommand{\braces}{>{\SplitArgument{1}{|}}r()}{\bracesaux#1}
\NewDocumentCommand{\bracesaux}{mm}{\bracesauxaux#1#2}
\NewDocumentCommand{\bracesauxaux}{mmmm}{%
  \chi_{#1}(1)\chi_{#2}(2)\chi_{#3}(1)\chi_{#4}(2)%
}

\begin{document}

\[
\braces(ab|cd)
\]

\end{document}

在此处输入图片描述

这是否比 更好\def,您来判断吧。

答案2

\NewDocumentCommand等(' ')的理念ltcmd是,它提供了一种轻松创建“类似 LaTeX”命令的方法。“类似 LaTeX”意味着主要使用 中的强制参数、{...}中的可选参数[..]、用于命令的“特殊替代”版本的星号等。但是,它也涵盖了一些更广泛的情况,最明显的是beamer-like 可选<..>、图片模式(如强制)(..)和逐字抓取(类似\verb但有一些区别)。这涵盖了文档中您想要的很多内容,但它确实不是包括对任意材料的深入解析。(灵活性的目的是覆盖足够的输入范围,以便大多数事情都可以使用来完成ltcmd。)

对于更复杂的情况,需要使用经典的 TeX 构造(或使用等效构造)来构建想法expl3。在大多数情况下,这将发生之内常规参数。例如,siunitx以“非标准”方式解析数字,但文档级命令使用签名\num定义。人们可能会将 TikZ 视为一个极端的例子:它定义了自己的输入语法,这绝对不是标准 LaTeX,但它是自洽的 - 但仅在环境或类似范围内可用。ltcmdO{}mtikzpicture

请注意,一个关键思想是ltcmd将文档命令语法与实现分开。这意味着,一般来说,如果您使用某种分隔的输入格式,那么单独实现仍然是明智的:这允许重新处理或重新使用一个或另一个方面,而不受另一个方面的限制。

我的建议是在定义非标准之前仔细考虑可预测性文档命令语法。

答案3


编辑:TeX - LaTeX Stack Exchange 成员数学 在评论中提请该答案初始版本的作者注意,下划线 ( _) 在 expl3 中没有类别 8(下标),但有类别 11(字母),因此不能在 expl3 中用于数学下标,所以在 expl3 中,需要使用隐式字符/_之一来代替下划线 ( )作为数学下标。\sb\c_math_superscript_token


故意将错误的工具用于某项任务并不是一个好主意。即使是 ltcmd/xparse,无论它有多现代,也不是万能的。有些任务最好用 ltcmd/xparse 以外的工具来完成,因为 ltcmd/xparse 不是为这些任务而设计的。所以不要专注于用 ltcmd/xparse 做这类事情。

再次使用\cs_new_protected:Npnexpl3/LaTeX 3 — 这样做没有错:

\ExplSyntaxOn
\cs_new_protected:Npn\braces(#1#2|#3#4){
    \ensuremath{
        \int \frac{\chi\sb#1(1)\chi\sb#2(1)\chi\sb#3(2)\chi\sb#4(2)}{r\sb{12}}~dV\sb1~dV\sb2
    }
}
\ExplSyntaxOff

我没有检查公式的输入是否可以改进。我保留了\ensuremath代码,因为您使用了它。如果实现由我决定,\ensuremath我会要求用户知道宏仅在数学模式下有效,而不是使用。\ensuremath是产生不良排版的陷阱。例如,您会看到人们试图通过排列\ensuremath在 TeX 处于水平模式时执行的命令来排版公式。这样,要排版的公式片段就会被排版,从而产生不良的排版,而不是让 TeX 切换到数学模式并一次性生成正确的公式。

[以下评论仅代表个人观点:]

除此之外,我还采取了一些可能在幕后切换排版模式的命令,这会引起一些困惑:当我撰写文档时,我必须决定并了解文本短语的类型,从而了解排版模式。当我想写一段文本时,我让 TeX 切换到水平模式。当我想输入数学时,我让 TeX 切换到数学模式(内联或显示)。我希望自己控制它。;-)



我没有找到在 ltcmd/xparse 中完美实现此目的的方法:

在实现某种参数分隔符处理时可能考虑的参数类型有:

  • rRdD,但是使用这些,您必须在参数之前和之后指定非空分隔符,除此之外,分隔符必须由单个标记组成。
  • eE,但像dD装饰参数是可选的,因此如果您省略了通过以下方式实现的分隔符e/ E/ d/ D-type-argument 实现的分隔符,除非您自己实现错误检查,否则可能不会收到错误消息。此外,如果 embellishment-argument 由多个标记组成或留空,则必须将其括在花括号中,而 TeX 的分隔参数则不需要这样做。

在您的特定情况下,当参数的分隔符由单个标记组成时,您可以应用带有分隔符的 D 类型参数,()默认|使用预处理器(如\SplitArgument或)\SplitList进行拆分|并将其结果提供给辅助宏,该宏处理两个参数并传递错误消息或制作四个错误消息,然后将其提供给另一个辅助宏,该宏处理四个参数并传递标记,扩展后将进入 TeX 的排版机制 - 这样,您可以自己管理错误处理和错误消息文本,并且如果)缺少右括号,可能会出现低级 TeX 错误:

\documentclass{article}

%---------------------------------------------------------------------
% With older LaTeX releases you need this, with recent ones you don't:
\ifx\IfBlankTF\UndEFiNEd
\ExplSyntaxOn
\cs_new_eq:NN \IfBlankTF \tl_if_blank:nTF
\ExplSyntaxOff
\fi
%---------------------------------------------------------------------

\NewDocumentCommand {\braces} {>{\SplitArgument{1}{|}}D(){|}} {%
  \bracesi#1%
}

\makeatletter
\NewDocumentCommand {\bracesi} {mm} {%
  \IfNoValueTF{#2}{\@secondoftwo}{%
    \IfBlankTF{#2}{\@secondoftwo}{%
      \expandafter\IfBlankTF\expandafter{\@firstoftwo{}#2}{\@secondoftwo}{%
        \IfBlankTF{#1}{\@secondoftwo}{%
          \expandafter\IfBlankTF\expandafter{\@firstoftwo{}#1}{\@secondoftwo}{%
            \@firstoftwo
          }%
        }
      }%
    }%
  }%
  {\bracesii#1#2}%
  {\message{^^J^^J!!!\on@line: Cannot get four arguments from the pattern!!!^^J}}%
}
\makeatother

\NewDocumentCommand {\bracesii} {mmmm} {%
  \ensuremath{%
    \int \frac{\chi_#1(1)\chi_#2(1)\chi_#3(2)\chi_#4(2)}{r_{12}} dV_1 dV_2%
  }%
}

\begin{document}

\braces(ab|cd)

% \braces()

% \braces(|)

% \braces(a|)

% \braces(|b)

% \braces(a|b)

% \braces(a|bc)

% \braces(ac|b)

% \braces(ac|)

% \braces(a|bc)

% \braces(ab) 

% \braces ab

% \braces c)

% \braces cd)

% \braces(AB|C % ) missing - low level error

% \braces(AB % ) missing - low level error

\end{document}

或者你定义一个带有 D 类型参数和分隔符(|默认为空的宏,它将|附加的该参数提供给一个辅助宏,该辅助宏处理一个 m 类型参数,表示和之间的东西是否(形成|两个参数和另外两个 m 类型参数,表示和之间的两个参数(|带有分隔符|和和默认为空的 D 类型参数,然后将所有这些参数提供给另一个处理四个参数并传递标记的宏,这些标记在扩展后将进入 TeX 的排版机制,或者传递一条错误消息 - 这样你可以自己管理错误处理和错误消息文本,并在缺少和/或)时获取低级 TeX 错误消息:|)

\documentclass{article}

%---------------------------------------------------------------------
% With older LaTeX releases you need this, with recent ones you don't:
\ifx\IfBlankTF\UndEFiNEd
\ExplSyntaxOn
\cs_new_eq:NN \IfBlankTF \tl_if_blank:nTF
\ExplSyntaxOff
\fi
%---------------------------------------------------------------------

\makeatletter
\NewDocumentCommand {\braces} {D(|{}} {%
  \IfBlankTF{#1}{\@secondoftwo}{%
    \expandafter\IfBlankTF\expandafter{\@firstoftwo{}#1}{\@secondoftwo}{\@firstoftwo}%
  }%
  {\bracesi{\@firstoftwo}#1|}%
  {\bracesi{\@secondoftwo}{}{}|}%
}
\NewDocumentCommand {\bracesi} {mmmD|){}} {%
  #1{%
    \IfBlankTF{#4}{\@secondoftwo}{%
      \expandafter\IfBlankTF\expandafter{\@firstoftwo{}#4}{\@secondoftwo}{\@firstoftwo}%
    }%
  }{\@secondoftwo}%
  {\bracesii{#2}{#3}#4}%
  {\message{^^J^^J!!!\on@line: Cannot get four arguments from the pattern!!!^^J}}%
}
\makeatother

\NewDocumentCommand {\bracesii} {mmmm} {%
  \ensuremath{%
    \int \frac{\chi_#1(1)\chi_#2(1)\chi_#3(2)\chi_#4(2)}{r_{12}} dV_1 dV_2%
  }%
}

\begin{document}

\braces(ab|cd)

% \braces() % | missing - low level error

% \braces(|)

% \braces(a|)

% \braces(|b)

% \braces(a|b)

% \braces(a|bc)

% \braces(ac|b)

% \braces(ac|)

% \braces(a|bc)

% \braces(ab) % | missing - low level error

% \braces ab % ) missing - low level error

% \braces c)

% \braces cd)

% \braces(AB|C % ) missing - low level error

% \braces(AB % | missing - low level error

\end{document}

答案4

问题中的代码引入了额外的空格。下面,使用 some 删除了这些空格%

然后,对于\braces(abc|xyz)cz不在下标中,如下所示。

\documentclass[border=6pt]{standalone}
\def\braces(#1#2|#3#4){%
    \ensuremath{%
        \int \frac{\chi_#1(1)\chi_#2(1)\chi_#3(2)\chi_#4(2)}{r_{12}} dV_1 dV_2%
    }%
}
\begin{document}
\braces(abc|xyz)
\end{document}

在此处输入图片描述

即使有\braces(a{bc}|x{yz})c和也不z在下标中,如下所示。

\documentclass[border=6pt]{standalone}
\def\braces(#1#2|#3#4){%
    \ensuremath{%
        \int \frac{\chi_#1(1)\chi_#2(1)\chi_#3(2)\chi_#4(2)}{r_{12}} dV_1 dV_2%
    }%
}
\begin{document}
\braces(a{bc}|x{yz})
\end{document}

在此处输入图片描述

原因是周围没有牙套#1原因是周围\chi_#1等等。

如果添加括号\chi_{#1},则cz位于下标中。

现在假设ab应该放在第一个下标和xy第三个下标中。然后\braces({ab}c|{xy}z)就可以使用了,如下所示。

\documentclass[border=6pt]{standalone}
\def\braces(#1#2|#3#4){%
    \ensuremath{%
        \int \frac{\chi_{#1}(1)\chi_{#2}(1)\chi_{#3}(2)\chi_{#4}(2)}{r_{12}} dV_1 dV_2%
    }%
}
\begin{document}
\braces({ab}c|{xy}z)
\end{document}

在此处输入图片描述

现在假设c|d应该放在第二个下标中。代码\braces({ab}c|d|{xy}z)给出的结果如下。

\documentclass[border=6pt]{standalone}
\def\braces(#1#2|#3#4){%
    \ensuremath{%
        \int \frac{\chi_{#1}(1)\chi_{#2}(1)\chi_{#3}(2)\chi_{#4}(2)}{r_{12}} dV_1 dV_2%
    }%
}
\begin{document}
\braces({ab}c|d|{xy}z)
\end{document}

在此处输入图片描述

可以使用 来修复此问题\braces({ab}{c|d}|{xy}z),如下所示。

\documentclass[border=6pt]{standalone}
\def\braces(#1#2|#3#4){%
    \ensuremath{%
        \int \frac{\chi_{#1}(1)\chi_{#2}(1)\chi_{#3}(2)\chi_{#4}(2)}{r_{12}} dV_1 dV_2%
    }%
}
\begin{document}
\braces({ab}{c|d}|{xy}z)
\end{document}

在此处输入图片描述

此时,使用\braces{ab}{c|d}{xy}{z}4 对括号进行输入可能会更方便。这种语法可以用\newcommand或轻松定义\NewDocumentCommand轻松定义此类语法。

相关内容