Cleveref 无法正确排序嵌套列表

Cleveref 无法正确排序嵌套列表

我想使用 cleveref 自动对交叉引用进行排序,但出于某种原因,它无法正确对嵌套枚举环境中的标签进行排序。这是我简化的代码:

\documentclass{article}

\usepackage[sort]{cleveref}

\begin{document}
\begin{enumerate}
    \item foo
    \begin{enumerate}
        \item 1 \label{1}
        \item 2 \label{2}
    \end{enumerate}
    \item bar
    \begin{enumerate}
        \item 3 \label{3}
    \end{enumerate}
\end{enumerate}

The reference comes out as: \cref{1,2,3}
\end{document}

我得到的结果是“引用结果为:项目 1a、2a 和 1b”,而不是我预期的“项目 1a、1b 和 2a”。似乎 cleveref 是从右到左排序的,所以 2a 放在 1b 之前,因为 a<b。我该如何修复它?

更新:我当时给软件包作者发了一封电子邮件,但一直没有收到答复。

答案1

通常,latex 中的计数器在初始化时会引用其父计数器:\newcounter{NameOfTheNewCounter}[NameOfTheOtherCounter]。这样,该\label命令可以访问树结构中的所有父级及其数字索引值。Cleveref 利用了这一事实,并在\label使用该命令时记下整个结构。这会写入.aux文件中。因此对于1.3.4 小节使用标签\label{aSubSection},这将是:

 \newlabel{aSubSection@cref}{{[subsubsection][4][3,1]1.3.4}{1}}

因此,排序时,cleveref 只是按相反顺序比较索引4, 3, 1。根本没有字典顺序 - clever cleveref!

现在,您偶然发现了 cleveref 中的一个错误。在发布修复程序之前,这里有一个解决方法:

\documentclass{article}

\usepackage[sort]{cleveref}

\makeatletter
\let\cref@old@resetby\cref@resetby%
\def\cref@resetby#1#2{
  \let#2\relax%
  \ifnum\pdfstrcmp{#1}{enumii}=\z@
    \def#2{enumi}%
  \fi%
  \ifnum\pdfstrcmp{#1}{enumiii}=\z@
    \def#2{enumii}%
  \fi%
  \ifnum\pdfstrcmp{#1}{enumiv}=\z@
    \def#2{enumiii}%
  \fi%
 \ifnum\pdfstrcmp{#1}{enumv}=\z@
    \def#2{enumiv}%
  \fi%
 \ifx#2\relax%
    \cref@old@resetby{#1}{#2}
 \fi}%
\makeatother

\begin{document}

\begin{enumerate}
    \item foo
    \begin{enumerate}
        \item 1 \label{1}
        \item 2 \label{2}
        \begin{enumerate}
        \item 5 \label{7}
        \item 3 \label{4}
        \end{enumerate}
    \end{enumerate}
    \item bar \label{6}
    \begin{enumerate}
        \item 3 \label{3}
        \begin{enumerate}
        \item 5 \label{5}
        \end{enumerate}
    \end{enumerate}
\end{enumerate}

The reference comes out as: \cref{1,2,3}, \cref{1,2} \cref{2,3}
and \cref{1,2,3,4,5,6,7}. As they should!

\end{document}

该错误基本上是使用了错误的字符串比较函数(使用 \ifx 测试参数值)——在乳胶中比较字符串可能非常困难!

输出结果如下:

示例输出

答案2

事实证明@Karalga 对此错误原因的解释是正确的,但并没有触及问题的根源。嵌套列表标签的排序被破坏了完全独立的原因!

@Karalga 的解决方法意外地修复了这两个问题,但我没有完全理解原因。不幸的是,我无法在包中使用该方法,因为它依赖于\pdfstreq内部方法,而内部方法仅存在于(足够新的版本)中pdftex

字符串比较cleveref使用了一种技术应该工作。(它甚至是当时解释的技术之一引用 stackchange 答案在枚举列表计数器名称的特定情况下,比较会失败,原因对我来说仍然有些神秘,但这可能与计数器名称获取奇怪的 catcode 有关。

然而,更大的问题是,在重置嵌套列表中的枚举计数器时,LaTeX 根本不使用计数器重置列表机制!重置是在enumeratelist环境定义内部硬编码的。因此,即使字符串比较有效,cleveref仍然无法检测到枚举计数器被更高级别的枚举计数器重置。

鉴于此,唯一合理的修复方法是将枚举硬编码为cref@resetby宏中的特殊情况。(以及修复字符串比较以使其与 catcode 无关。)

我已经在最新版本(0.20)中完成了此操作,可从我的网站。我会在做完更多测试后才将其上传到 CTAN,因为这个错误有点难以察觉。

答案3

感谢@Karalga 解决了我(完全相同)的问题。

我将其改为列表,以使其不那么愚蠢。(也许我们可以使用类似的包将其更改为键值对pgfkeys

这是我的代码。

\makeatletter
\def\cref@resetby@list{enumv,enumiv;enumiv,enumiii;enumiii,enumii;enumii,enumi;}
\let\cref@old@resetby\cref@resetby
\def\cref@resetby#1#2{%
    \let#2\relax%
    \def\@eatall##1\relax{}%
    \def\@tmpcode##1,##2;{%
        \let\@next\@eatall
        \ifx,##1,\else
            \ifnum\pdfstrcmp{#1}{##1}=\z@
                \def#2{##2}%
            \else
                \let\@next\@tmpcode
            \fi
        \fi
        \@next
    }%
    \expandafter\@tmpcode\cref@resetby@list,;\relax
    \ifx#2\relax%
        \cref@old@resetby{#1}{#2}%
    \fi
}
\makeatother

可以通过重新定义来简单地扩展映射\cref@resetby@list,它应该是形式为<ChildEnvironment-1>,<ParentEnvironment-1>;<ChildEnvironment-2>,<ParentEnvironment-2>;等的列表。列表的最后一个符号必须<ChildEnvironment>是分号 (;),列表以空字符结尾。我将始终将其附加,;到列表末尾,因此无需担心列表的结尾。

这个想法很简单:

  1. 检查<ChildEnvironment>列表是否为空。如果是,则删除列表的其余部分。
  2. 检查<ChildEnvironment>列表中是否等于#1。如果是,#2则定义并删除列表的其余部分。
  3. 移至下一对并继续检查。

相关内容