为什么 LaTeX3 命令中有这么多下划线?

为什么 LaTeX3 命令中有这么多下划线?

这个问题源于我的评论以及随后的聊天讨论。

我对 TeX 一无所知,但我可以轻松理解如下命令:

\let\oldmacro\somemacro

然而

\cs_set_eq:NN

对我来说不是那么清楚。

尤其是命令名称中的下划线让我眼花缭乱。例如,为什么_选择 而不是?\csSetEq

而且,为什么:NN需要?为什么需要在命令名中添加语法信息?

拥有不言自明的命令不是 LaTeX 的优势之一吗?

答案1

下划线

\csSetEq不起作用:对于此类基本的内部命令,您需要普通用户无法意外覆盖的名称。这就是为什么内部 LaTeX2e 命令具有@上下文用途!以及下划线:\c_anch_sidebars_current \c!bottomoffset

expl3 不能使用,@因为它很容易与现有命令和包发生冲突,并且来自其他可能的非字母(!,?,*,+,...)在我看来下划线不是一个坏选择:当你习惯了它,你可以阅读许多命令,如句子。\cs_set_eq= commands set equal,,\tl_if_exist\regex_extract_once

:NN 语法

阅读 (La)TeX 代码时的一个问题是,很难看出命令有多少个参数(如果有的话)。这使得很难理解命令的处理过程以及调试由于缺少或错误参数而导致的错误。例如,命令\@sect需要 6 个参数,但您必须返回定义才能看到这一点。使用 :NN 语法,您可以获得更好的概览。

答案2

您提到的函数的名称由三部分组成:csseteq

  • cs是“模块名称”:该函数处理函数(用于定义和类似的东西);cs代表“控制序列”,可能不是最好的名字,但现在改变已经太晚了。

  • set是主要的动作类型;在这个模块中,基本上有三种动作类型:newsetgset第一种是全局定义新函数,检查名称是否已被使用;第二种和第三种是使用检查,区别在于set在本地操作(在当前组的范围内),而gset定义或重新定义在全局执行。

  • eq是定义的执行方式,代表“平等”。

因此,该名称可以读作“本地定义一个函数为某个已经存在的函数的副本”。

接下来是“签名”,在本例中是:NN,这意味着该函数后面\cs_set_eq:NN应该跟着两个不带括号的函数名。

它不仅使可视化语法检查变得简单,而且在内部用于定义变体我们可以将其与其他工具、标准 LaTeX 和以下内容进行比较etoolbox

\let\foo\baz
\letcs\foo{baz}
\cslet{foo}\baz
\csletcs{foo}{baz}

这四个命令执行相同的任务;当然,etoolbox当需要从其他宏的参数构造一个或两个名称时,将使用 提供的三个命令。expl3我们有c参数类型来表示控制序列,其名称应由括号参数中的标记组成(当然,在内部使用\csname...\endcsname):

\cs_set_eq:NN \carla_foo:n \carla_baz:n
\cs_set_eq:Nc \carla_foo:n {carla_baz:n}
\cs_set_eq:cN {carla_foo:n} \carla_baz:n
\cs_set_eq:cc {carla_foo:n} {carla_baz:n}

并且团队在定义它们时无需思考:在以原语的形式定义第一个之后(\tex_let:D,但这无关紧要),所需要的只是

\cs_generate_variant:Nn \cs_set_eq:NN { Nc, cN, cc }

您可以将其与相应的代码进行比较etoolbox.sty

\newrobustcmd{\cslet}[2]{%
  \expandafter\let\csname#1\endcsname#2}

% {<cstoken>}{<csname>}

\newrobustcmd{\letcs}[2]{%
  \ifcsdef{#2}
    {\expandafter\let\expandafter#1\csname#2\endcsname}
    {\undef#1}}

% {<csname>}{<csname>}

\newrobustcmd*{\csletcs}[2]{%
  \ifcsdef{#2}
    {\expandafter\let
     \csname#1\expandafter\endcsname
     \csname#2\endcsname}
    {\csundef{#1}}}

变体是 的一个非常好的功能expl3,它们依赖于具有正确签名的函数。请\cs_generate_variant:Nn在网站上查找以了解其用途。

答案3

如果你有

\let\oldmacro\somemacro

但是您的宏名称被作为名称传递(或生成),所以您有{oldmacro}然后{newmacro} 你可以去

\expandafter\let\csname oldmacro\expandafter\endcsname\csname newmacro\endcsname

但这不是很清楚,并且当嵌入到一些较大的宏集中时很容易出错并引入奇怪的错误。

L3 版本你只需要一个变体分配,该分配将两个名称作为支撑组而不是两个标记,因此:

\cs_set_eq:cc{oldmacro}{newmacro}

这些变体形式要么已经提供,要么可以轻松生成,用于以:NN您可以指定cc变体或:Nx变体(其中第二个参数首先是 edef-expanded)等结尾的任何命令。

选择_骆驼式大小写不太具结构性,而是一种设计选择,但骆驼式大小写效果较差,因为您无法阻止在文档中间使用骆驼式大小写命令,但_设置方式@与纯 tex 和 latex2e 类似,只是代码部分中的一个字母。

答案4

您不应该将其与用户代码进行比较,\let\command\original而应该将其与一些内部代码进行比较。您似乎很担心,好像您可能需要每天使用这些命令。那是内部代码。就像我们现在有内部代码一样,您可能也会害怕。

这是使素数按预期工作的代码:f'(x)f'''(x)因此而工作

\def\active@math@prime{^\bgroup\prim@s}
{\catcode`\'=\active \global\let'\active@math@prime}
\def\prim@s{%
  \prime\futurelet\@let@token\pr@m@s}
\def\pr@m@s{%
  \ifx'\@let@token
    \expandafter\pr@@@s
  \else
    \ifx^\@let@token
      \expandafter\expandafter\expandafter\pr@@@t
    \else
      \egroup
    \fi
  \fi}
\def\pr@@@s#1{\prim@s}
\def\pr@@@t#1#2{#2\egroup}

类似的 expl3 版本如下

\cs_new_protected:Nn \math_prime: { \math_superscript:w \math_prime_next: }
\cs_new_protected:Nn \math_prime_next:
 {
  \math_prime_symbol
  \peek_charcode_remove_ignore_spaces:NTF '
   { \math_prime_next: }
   {
    \token_if_eq_meaning:NNTF \l_peek_token \c_math_superscript_token
     { \math_prime_remove_superscript:Nn }
     { \math_superscript_end: }
   }
 }
\cs_new_protected:Nn \math_prime_remove_superscript:Nn { #2 \math_superscript_end: }
\char_set_active_eq:NN ' \math_prime:

使用 expl3 似乎更加一目了然(尽管您可能需要学习一点)。事实上,我认为这种代码使内部结构更加易于理解。

PS:我还没有检查代码是否正确,而且它无法编译,因为它确实使用了一些未定义的命令,但我们的想法是,它实际上比那些\pr@@@t和显示了更多正在发生的事情\pr@@@s

相关内容