局部问题与全局问题的解释

局部问题与全局问题的解释

我尝试标记\description环境,但发现了一个奇怪的问题。当我重新定义\descriptionlabel命令时,我将布尔变量设置为 true,但第二项之后我的变量为 false 值,因此我的标记工作不正确。我尝试调试命令,正如我所料,首先我有\list命令,第二项 - 我有两个\descriptionlabel命令,之后我有\endlist命令,所以一切都按预期工作,但为什么我的布尔变量没有设置为 true?请帮帮我,为什么我的变量会发生这种情况?非常感谢大家的帮助。

\documentclass{article}
\usepackage{tagpdf}
\tagpdfsetup{tabsorder=structure,uncompress,activate-all,interwordspace=true}
\ExplSyntaxOn
\makeatletter
\int_gzero_new:N \g__list_level_int
%commands for tagging of lists.
\let\start@list=\list
\let\end@list=\endlist
\let\desc@label=\descriptionlabel

\renewcommand{\list}[2]{
\int_case:nnF{\g__list_level_int}{{0}{}}{
%we need check,if we have items in previous level,and if yes,close mc and struct before starting of the list.
\bool_if:cTF{l__first_item_\int_use:N \g__list_level_int _bool}{\tagmcend\tagstructend
%We close previous LI and LBody
\tagstructend\tagstructend
\bool_set_false:c{l__first_item_\int_use:N \g__list_level_int _bool}
}
{}
\tagstructbegin{tag=LI}
\tagstructbegin{tag=LBody}
}
\int_gincr:N \g__list_level_int
\bool_if_exist:cTF{l__first_item_\int_use:N \g__list_level_int _bool}{}{\bool_new:c{l__first_item_\int_use:N \g__list_level_int _bool}}
\tagstructbegin{tag=L}
\start@list{#1}{#2}
}

\renewcommand{\endlist}{
\end@list
\bool_if:cTF{l__first_item_\int_use:N \g__list_level_int _bool}{
\tagmcend
\tagstructend
\tagstructend
\tagstructend
\bool_set_false:c{l__first_item_\int_use:N \g__list_level_int _bool}
}
{
%if we haven't items,but we have level more then one,we have sublist before,so we should close LI and LBody.
\int_case:nnF{\g__list_level_int-1}{{0}{}}{\tagstructend\tagstructend}
}
\int_gdecr:N \g__list_level_int
\tagstructend
}

\renewcommand{\descriptionlabel}[1]{
\bool_if:cTF{l__first_item_\int_use:N \g__list_level_int _bool}{
\message{true~value}
%we have an item before,so we must close the last item,LBody and LI.
\tagmcend\tagstructend\tagstructend\tagstructend}{
\message{false~value}
%this code executed twice,but must executed only the first time. Is it expl3 issue?
\bool_set_true:c{l__first_item_\int_use:N \g__list_level_int _bool}
}
\tagstructbegin{tag=LI}
\tagstructbegin{tag=Lbl}
\tagmcbegin{tag=Lbl}
\desc@label{#1}
\tagmcend
\tagstructend
\tagstructbegin{tag=LBody}
\tagstructbegin{tag=P}
\tagmcbegin{tag=P}
}
\makeatother
\ExplSyntaxOff

\begin{document}
\tagstructbegin{tag=Document}
\begin{description}
\item test
\item new test
\end{description}
\tagstructend
\end{document}

答案1

警告:我对的了解tagpdf几乎为零,所以我不能说它在这里是否使用正确。我只能说 TeX、LaTeX 和expl3。:-)

正如@UlrikeFischer所暗示的,您的布尔值似乎是在一个组内设置的。在将布尔值设为全局变量(+使用\bool_gset_false:c等)并修复我看到的各种问题后,它似乎可以按您的要求工作。

局部问题与全局问题的解释

如果你阅读article.cls,你会发现它确实如此:

\newenvironment{description}
               {\list{}{\labelwidth\z@ \itemindent-\leftmargin
                        \let\makelabel\descriptionlabel}}
               {\endlist}
\newcommand*\descriptionlabel[1]{\hspace\labelsep
                                \normalfont\bfseries #1}

因此,当 LaTeX 处理文档环境时,您的\descriptionlabel命令暂时以名称为人所知。然后,在 LaTeX 内核源代码(此处, )中,我们有:\makelabeldescriptionltlists.dtx

\def\@item[#1]{%
...
\sbox\@tempboxa{\makelabel{#1}}%
\global\setbox\@labels\hbox{...}
...
}

这是 的替换文本\@item,它实现了命令的核心\item\@tempboxa在 中使用\hbox{...}。因此,您的\descriptionlabel命令将在 的第二个参数中执行(名称为\makelabel,但这并不重要)\sbox。因此,它将在框内执行,因此在隐式组内。 有时候是这样的对于每个标签您的description环境。当\sbox命令完成其工作时,隐式组已经结束,这会自动恢复设置的所有变量和命令本地将组内的状态恢复到组启动时的状态(TeX 对所有组都这样做;特别地,这发生在所有用 TeX 基元创建的所有框中,例如、 或\hbox\vbox因为这样的框命令总是在隐式组内执行)。\vtop\vcenter

因此,如果您在 内使用\bool_set_true:N或,则设置将在 内完成(实际上, TeX 基元基于 ),并且其效果将在框结束时立即取消。这甚至在文本开始之前。因此,您的本地设置在下次检查之前不会保留。相反,在组内(包括框内)进行的全局设置会影响所有分组级别;当组结束时,它们的效果不会恢复。这就是为什么它可以按照您想要的方式与或类似的全局分配一起工作。\bool_set_false:N\descriptionlabel\sbox\hbox\sbox\item\bool_gset_true:N\bool_gset_false:N

确实,expl3不会查看变量名是否以l_或开头g_:这确实只适合我们这些普通人。对 TeX 来说,重要的是您使用的是本地分配还是全局分配(\bool_set_*vs.\bool_gset_*等)。

其他备注

请使用(如果可以)expl3命名约定和编码风格,如LaTeX3 编程语言LaTeX3 风格指南LaTeX3 界面。对我来说,在进行大量重新格式化之前,您的代码几乎无法使用。

在为现有函数定义新名称时也要使用\cs_new_eq:NN而不是\let,否则就有可能覆盖现有函数,并可能产生很难调试的副作用(使用此处选择的名称,发生这种情况的可能性相当高:\start@list\end@list\desc@label;最好使用expl3我下面所做的命名约定:\__aleksandr_orig_start_list:nn\__aleksandr_orig_end_list:\__aleksandr_orig_descriptionlabel:n)。

另一件可以让代码更容易使用的事情是:

\int_compare:nNnF { \g__list_level_int } = { 0 }
  { ... }

比下面这个更容易阅读:

\int_case:nnF { \g__list_level_int }
  { {0} {} }
  { ... }

在我看来。

\documentclass{article}
\usepackage{xparse}
\usepackage{tagpdf}
\tagpdfsetup{tabsorder=structure,uncompress,activate-all,interwordspace=true}

\ExplSyntaxOn

% The declaration is global. The integer is initially equal to 0 (see
% interface3.pdf).
\int_new:N \g__aleksandr_list_level_int

\cs_new_eq:NN \__aleksandr_orig_start_list:nn \list
\cs_new_eq:NN \__aleksandr_orig_end_list: \endlist
\cs_new_eq:NN \__aleksandr_orig_descriptionlabel:n \descriptionlabel

% Simple macro to reduce redundancy and the length of some lines. It expands
% to the list boolean variable name for the current level **without its
% backslash**.
\cs_new:Npn \__aleksandr_list_level_bool_name:
  {
    g__first_item_ \int_use:N \g__aleksandr_list_level_int _bool
  }

\RenewDocumentCommand \list { }
  {
    \int_compare:nNnF { \g__aleksandr_list_level_int } = { 0 }
      {
        % We need check, if we have items in previous level,and if yes, close mc
        % and struct before starting of the list.
        \bool_if:cT { \__aleksandr_list_level_bool_name: }
          {
            \tagmcend \tagstructend
            %We close previous LI and LBody
            \tagstructend \tagstructend
            \bool_gset_false:c { \__aleksandr_list_level_bool_name: }
          }

        \tagstructbegin { tag=LI }
        \tagstructbegin { tag=LBody }
      }

    \int_gincr:N \g__aleksandr_list_level_int
    \bool_if_exist:cF { \__aleksandr_list_level_bool_name: }
      { \bool_new:c { \__aleksandr_list_level_bool_name: } }

    \tagstructbegin { tag=L }
    \__aleksandr_orig_start_list:nn
  }

\RenewDocumentCommand \endlist { }
  {
    \__aleksandr_orig_end_list:
    \bool_if:cTF { \__aleksandr_list_level_bool_name: }
      {
        \tagmcend
        \tagstructend
        \tagstructend
        \tagstructend
        \bool_gset_false:c { \__aleksandr_list_level_bool_name: }
      }
      {
        % If we haven't items, but we have level more then one, we have sublist
        % before, so we should close LI and LBody.
        \int_compare:nNnF { \g__aleksandr_list_level_int - 1 } = { 0 }
          { \tagstructend \tagstructend }
      }
    \int_gdecr:N \g__aleksandr_list_level_int
    \tagstructend
  }

\RenewDocumentCommand \descriptionlabel { m }
  {
    \bool_if:cTF { \__aleksandr_list_level_bool_name: }
      {
        \iow_term:n { True~value }
        % We have an item before, so we must close the last item, LBody and LI.
        \tagmcend \tagstructend \tagstructend \tagstructend
      }
      {
        \iow_term:n { False~value }
        % This code is only executed once.
        \bool_gset_true:c { \__aleksandr_list_level_bool_name: }
      }
    \tagstructbegin {tag=LI}
    \tagstructbegin {tag=Lbl}
    \tagmcbegin {tag=Lbl}
    \__aleksandr_orig_descriptionlabel:n {#1}
    \tagmcend
    \tagstructend
    \tagstructbegin {tag=LBody}
    \tagstructbegin {tag=P}
    \tagmcbegin {tag=P}
  }

\ExplSyntaxOff

\begin{document}

\tagstructbegin{tag=Document}
\begin{description}
\item test
\item new test
\end{description}
\tagstructend

\end{document}

这将打印:

False value
True value

在终端上,我相信这正是您所期望的。

相关内容