什么是 \tl 以及如何使用它?

什么是 \tl 以及如何使用它?

我需要对以下代码进行一些修改,以便更好地适应我的用例,但我真的很难学习和理解它的一些语言。像这样的标记\tl太短了,无法在搜索中返回任何有意义的结果,我甚至不知道它代表什么,无法尝试通过它找到它。

以下是代码:

% code to create chapters, verses, and cross references
\ExplSyntaxOn

\int_new:N \crossref_int
\int_new:N \vs_int
\tl_new:N \crossref_tl

% insert a cross reference
\NewDocumentCommand {\crossref} {m}
  {
    \int_compare:nNnT { \crossref_int } > { 25 }
      {
        \int_set:Nn \crossref_int { 0 }
      }
    \int_incr:N \crossref_int
% NOT PRESENTLY REQUIRING LETTERED SUPERSCRIPTS
%    \textsuperscript{ \emph { \int_to_alph:n { \crossref_int } } }
    \tl_if_empty:NF \crossref_tl
      {
        \tl_gput_right:Nn \crossref_tl { ~ }
      }
    \tl_gput_right:Nx \crossref_tl
      {
% NOT PRESENTLY REQUIRING LETTERED SUPERSCRIPTS
%        \exp_not:N \textsuperscript
%          {
%            \exp_not:N \emph { \int_to_alph:n { \crossref_int } }
%          }
        \, #1
      }
  }

%Sample output
% insert chapter marker
\NewDocumentCommand {\ch} {m}
  {
    \int_gset:Nn \vs_int {1}
    \lettrine [findent=0.5em,nindent=0em] { #1 } {}
  }

% output cross references from previous verse and insert verse marker
\NewDocumentCommand {\vs} {m}
  {
    \tl_if_empty:NF \crossref_tl
      {
        \sidebar
          {
            \textbf { \int_use:N \vs_int } \, \tl_use:N \crossref_tl
          }
      }
    \int_gset:Nn \vs_int { #1 }
    \tl_gclear:N \crossref_tl
    \textsuperscript { #1 \, }
  }

% output any remaining cross references
\AtEndDocument
  {
    \tl_if_empty:NF \crossref_tl
      {
        \sidebar
          {
            \textbf { \int_use:N \vs_int } \, \tl_use:N \crossref_tl
          }
      }
  }

\ExplSyntaxOff

在此代码中的某个地方,交叉引用大概被放置在边缘处,但目前在该过程中有几件事情无法正常工作,我需要进行修复,包括这仅适用于偶数页,它不会使参考编号变为粗体(并且奇怪的是,当我尝试\emph在代码中添加时,交叉引用全部落在文本列中而不是边缘处),并且我希望能够将第二个文本列的引用发送到边缘的底部 - 如果可能的话。

感谢那些能够帮助我理解此代码的人!

答案1

没有\tl。我认为你可能会有点困惑,因为你看到了 expl3 语法(因此代码周围是\ExplSyntaxOn...) 。可以通过键入简短概述和详细但不一定容易理解的说明\ExplSyntaxOff来找到此内容的关键文档。Alan Xiang 的texdoc expl3texdoc interface3LaTeX3 教程是一个更温和的介绍和一个良好的起点。

但是让我们一点一点地浏览您的代码以尝试理解它:

\ExplSyntaxOn

这是我们处于 expl3 模式的信号。在此模式下,命令名称可以包含_:因此您认为的\tl实际上是多个以 开头\tl但内容更多的不同命令名称。此外,此模式下的所有空格都将被忽略(如果您需要文字空格,~将提供该空格,\nobreakspace如果您需要不间断空格,则可以使用 while。

\int_new:N \crossref_int
\int_new:N \vs_int
\tl_new:N \crossref_tl

这里我们声明了一些新变量¹。标准命令的命名是使用\范围_模块_描述_类型⟩ 来命名这些。可惜的是,除了将类型放在末尾之外,此代码没有遵循该约定。我们还看到了一些命令。命令的 expl 3 约定是\模块_描述:参数规范⟩ 至少在内部是遵循的。这:有助于注意到什么时候是命令而不是变量,这至少有助于解决 TeX 编程的神秘性的一个方面。所有 expl3 命令将要:,即使它们不接受参数。在这里我们看到一个 arg-spec,N 它引用单个标记,这里是 LaTeX 控制序列名称。所以我们在这里所做的是定义两个新的整数变量和一个标记列表(这就是\tl您到处看到的)。

% insert a cross reference
\NewDocumentCommand {\crossref} {m}

这里我们定义了一个新的文档命令,\crossref带有一个强制参数。这大致相当于您可以在命令行中输入\newcommand*{\crossref}[1]{...}以下内容来了解​​详细信息。\NewDocumentCommandtexdoc xparse

  {
    \int_compare:nNnT { \crossref_int } > { 25 }
      {
        \int_set:Nn \crossref_int { 0 }
      }

现在我们实际上已经开始了命令定义。我们正在进行整数比较。这里的参数规范是nNnT。sn指的是用括号分隔的参数。N再次是一个单个标记,尽管这次是,>并且T是的特殊情况,n它指的是比较为真时要执行的代码块(还有F和两者都可以用于比较,因此,例如,\int_compare:nNnTF它在比较代码后占用两个代码块来执行真和假分支。

因此在这种情况下,如果\crossref_int大于 25,我们将其重置为 0。因为正如我们稍后会看到的,我们使用它来生成字母索引,所以这是完全合理的。

    \int_incr:N \crossref_int

在这里,我们增加了 的值\crossref_int

% NOT PRESENTLY REQUIRING LETTERED SUPERSCRIPTS
%    \textsuperscript{ \emph { \int_to_alph:n { \crossref_int } } }
    \tl_if_empty:NF \crossref_tl
      {
        \tl_gput_right:Nn \crossref_tl { ~ }
      }

现在,标记列表开始发挥作用。正如其名称所示,标记列表是标记列表。在本例中,我们查看 的值\crossref_tl并查看它是否为空。如果是不是(注意F上的参数说明\tl_if_empty),然后我们调用\tl_gput_right:在右侧添加一个空格。gingput表示全局,这是遵循 expl3 约定会很有用的地方之一。 TeX 的局限性之一是,如果您对其寄存器变量进行全局和本地操作,则可能会导致内存泄漏。 Knuth 对此的建议是采用一种约定,例如,只对偶数寄存器执行全局操作,对奇数寄存器执行本地操作。²

    \tl_gput_right:Nx \crossref_tl
      {
% NOT PRESENTLY REQUIRING LETTERED SUPERSCRIPTS
%        \exp_not:N \textsuperscript
%          {
%            \exp_not:N \emph { \int_to_alph:n { \crossref_int } }
%          }
        \, #1
      }
  }

然后是此命令的主要工作。将新的标记列表放入标记列表的右侧\crossref_tl。请注意,这里的参数列表表示Nx我们x想要在附加列表之前扩展列表的内容。这将允许注释的代码在添加到标记列表之前执行,这是我们想要的,因为我们关心\crossref_int执行命令时的值。如果我们没有这样做,那么我们最终会得到一个标记列表,例如,

d注1d笔记2d注 3d注 4

而不是期望的

A注1b笔记2C注 3d注 4

%Sample output
% insert chapter marker
\NewDocumentCommand {\ch} {m}
  {
    \int_gset:Nn \vs_int {1}
    \lettrine [findent=0.5em,nindent=0em] { #1 } {}
  }

这个很简单。我们定义\ch采用单个强制参数,并使用lettrineclass³ 以首字下沉样式设置章节编号。我们还(全局)将计数器设置\vs_int为 1。

% output cross references from previous verse and insert verse marker
\NewDocumentCommand {\vs} {m}
  {
    \tl_if_empty:NF \crossref_tl
      {
        \sidebar
          {
            \textbf { \int_use:N \vs_int } \, \tl_use:N \crossref_tl % ❶
          }
      }
    \int_gset:Nn \vs_int { #1 } % ❷
    \tl_gclear:N \crossref_tl % ❸
    \textsuperscript { #1 \, } % ❹
  }

因此,在这里,我们\vs还定义了一个强制参数,即诗句编号,它被设置为诗句文本前的上标❹。但在此之前,我们将输出交叉引用(如果有)。我们查看\crossref_tl它是否为空(\tl_if_empty),如果不是(因此F在参数规范中),我们使用\sidebar以粗体显示来设置的值\vs_int(我们必须使用\int_use:N来获取此目的的值),然后设置标记列表的内容\crossref_tl(再次,\tl_use:N是获取该值所必需的❶)。然后,我们将的值设置为\vs_int传递给参数\vs❷的数字并清除\crossref_tl标记列表❸。

% output any remaining cross references
\AtEndDocument
  {
    \tl_if_empty:NF \crossref_tl
      {
        \sidebar
          {
            \textbf { \int_use:N \vs_int } \, \tl_use:N \crossref_tl
          }
      }
  }

最后一段代码是为了确保剩余的交叉引用输出到文档末尾。这是使用钩子完成的\AtEndDocument(尽管较新的风格应该是这样写\AddToHook{enddocument}(LaTeX 为类和包编写者添加了广泛的“钩子”机制。请参阅texdoc source2e详细信息)。设置此项的代码与定义时使用的代码基本相同,\vs因此我不会再多说了。

\ExplSyntaxOff

然后我们关闭 expl 语法,以便文档的其余部分没有问题。

至于您最初的问题,我猜您要解决的问题在于 的定义\sidebar,无论它来自哪里。


  1. TeX 没有确切地有变量。有 256 个寄存器各种类型(计数器\count– 用普通的 TeX 语言来表示,它保存整数、长度(普通的 TeX 称之为\dimens)、胶水(普通的 TeX 称之为\skips,它们本质上是可以拉伸或收缩的长度)、标记列表、盒子、muskips、插入、读写流和数学字体系列。其中大多数限制为 256 个寄存器,一些寄存器被保留(例如,\count0页码)。TeX 中的大多数“变量”只是宏,这让生活有点困难,expl3 试图对事物施加一些秩序,尽管正如我们将看到的,这种秩序并没有被强制执行)。

  2. 或者可能恰恰相反。我要回忆一下 1986 年的往事。

  3. 这是一个很好的例子正确的使用lettrine。由于首字下沉没有任何语义值,因此实际上不应直接在文本中设置,而应将其作为其他代码的一部分。无需太多努力,就可以使用 的lettrine功能自动设置第一个段落的第一个字母,\chapter而无需任何额外的标记。

相关内容