引用手动设置标签的枚举项

引用手动设置标签的枚举项

在我的文档中,我列举了几个需要引用的项目。它们用简单的 (1)、(2)... 进行编号,但我还需要 (2')。为了更好地引用,我使用了enumitem,但在更改一个标签时会失败:

\documentclass{article}
\usepackage{enumitem}
\begin{document}

\begin{enumerate}[label=\textnormal{(\arabic*)}]
    \item text1\label{itm:1}.
    \item text2\label{itm:2}.
    \item[(2')] manually different label\label{itm:2b}.
    \item text3\label{itm:3}.
\end{enumerate}
Referencing to \ref{itm:1}\ref{itm:2}\ref{itm:2b}\ref{itm:3}.

\end{document}

最后一行输出如下内容:

Referencing to (1)(2)(2)(3).

我该如何修复这个问题?

答案1

你可以像这样定义它:

\documentclass{article}
\usepackage{enumitem}
\makeatletter
\newcommand{\mylabel}[2]{#2\def\@currentlabel{#2}\label{#1}}
\makeatother
\begin{document}
\begin{enumerate}[label=\textnormal{(\arabic*)}]
    \item text1\label{itm:1}.
    \item text2\label{itm:2}.
    \item[\mylabel{itm:2b}{(2')}] manually different label.
    \item text3\label{itm:3}.
\end{enumerate}
Referencing to \ref{itm:1}\ref{itm:2}\ref{itm:2b}\ref{itm:3}.

\end{document}

enter image description here

答案2

这是一篇旧帖子,但所有现有的答案都定义了一些新命令,然后需要额外的调整才能使标签(2')起作用。更好的方法是一次性“修复”标签,以便在以“正常”方式使用环境时引用能够按预期工作enumerate

\begin{enumerate}
    \item text1\label{itm:1}.
    \item text2\label{itm:2}.
    \item[(2')] manually different label\label{itm:2b}.
    \item text3\label{itm:3}.
\end{enumerate}
Referencing to \ref{itm:1}\ref{itm:2}\ref{itm:2b}\ref{itm:3}.

\@currentlabel正如 Ambika Vanchinathano 所说,要做到这一点,我们需要重新定义每当给出可选参数时的值\item。以下代码行定义了一个新的 item 命令,\myItem它正是这样做的(我使用解析包,因为它提供了一种更清晰的方式来处理可选参数):

\let\realItem\item  % save the original item command
\NewDocumentCommand\myItem{ o }{%
   \IfNoValueTF{#1}%
      {\realItem}% add an item
      {\realItem[#1]\def\@currentlabel{#1}}% add an item and update label
}

也就是说,\myItem使用原始\item命令设置标签,并\@currentlabel在给定可选参数时更新。实际上,我们需要将这些命令包装在里面,\makeatletter...\makeatother但上面的代码基本上就是我们所需要的。

我们可以告诉枚举环境使用\myItem而不是\item使用\setlist

\setlist[enumerate]{before=\let\item\myItem}% make \item=\myItem

如果您使用超链接包裹。

把所有这些放在一起,下面是完整的代码。我已将 MWE 中的所有格式放入\setlist命令中:

\documentclass{article}
\usepackage{xparse}
\let\realItem\item % save a copy of the original item
\makeatletter
\NewDocumentCommand\myItem{ o }{%
   \IfNoValueTF{#1}%
      {\realItem}% add an item
      {\realItem[#1]\def\@currentlabel{#1}}% add an item and update label
}
\makeatother

\usepackage{enumitem}    
\setlist[enumerate]{
    before=\let\item\myItem,       % use \myItem in enumerate
    label=\textnormal{(\arabic*)}, % format the label
    widest=(2')                    % set the widest label
}

\begin{document}

  \begin{enumerate}
      \item text1\label{itm:1}.
      \item text2\label{itm:2}.
      \item[(2')] manually different label\label{itm:2b}.
      \item text3\label{itm:3}.
  \end{enumerate}
  Referencing to \ref{itm:1}\ref{itm:2}\ref{itm:2b}\ref{itm:3}.

\end{document}

这将产生预期的输出:

enter image description here

实际上,enumerate您可能不想重新定义环境来像这样工作,而是希望使用以下命令创建一个新的enumerate环境:\newlist枚举项包裹。

实际上,我有点惊讶,当有一个可选参数时,枚举环境不会\@currentlabel像这样重新定义......\item

答案3

如果使用hyperref,最简单的方法是打印素数有一个指向正确项目的链接是通过Ambika Vanchinathan添加虚拟计数器(即\newcounter{dummy}在序言中和\refstepcounter{dummy}的定义中添加\mylabel)稍微修改解决方案。

然而,我个人宁愿通过以下方式重新定义\item(而不是\label):

\makeatletter
\newcommand\myitem[1][]{\item[#1]\refstepcounter{dummy}\def\@currentlabel{#1}}
\makeatother

根据此定义,手动标记的物品应被引入为

\myitem[(2')]\label{itm:2b}

也就是说,相对于通常的引入事物的方式,变化很小,因此新定义更容易记住。我还会通过以下方式定义数学模式素数

\newcommand*{\mprime}{\ensuremath{'}}

完成 MWE:

\documentclass{article}
\newcounter{dummy}
\usepackage{hyperref}
\usepackage{enumitem}
\makeatletter
\newcommand\myitem[1][]{\item[#1]\refstepcounter{dummy}\def\@currentlabel{#1}}
\makeatother
\newcommand*{\mprime}{\ensuremath{'}}

\begin{document}
\begin{enumerate}[label=\textnormal{(\arabic*)}]
    \item text1\label{itm:1}.
    \item text2\label{itm:2}.
    \myitem[(2\mprime)]\label{itm:2b} manually different label.
    \item text3\label{itm:3}.
\end{enumerate}
Referencing to \ref{itm:1}\ref{itm:2}\ref{itm:2b}\ref{itm:3}.

\end{document}

答案4

您可以像这样动态地执行此操作,而不必自己输入“2”;只需传递后缀(例如')作为参数即可。此解决方案适用于有和没有hyperrefcleveref和的情况enumitem,基本上依赖于新\labelsuffix命令:

\documentclass{article}
\usepackage{xpatch}

\usepackage{hyperref}
\usepackage{cleveref}
\usepackage{enumitem}

% latex.ltx
% \def\item{ 
% - sets \@noitemargtrue if no argument
% - calls \@item
% \def\@item[#1]{
% - checks \if@noitemarg
% - calls \refstepcounter only if true
% ==> \item[] does not call \refstepcounter
% \def\refstepcounter#1{\stepcounter{#1}
% - defines \@currentlabel
% \def\label#1{\@bsphack
% - uses \@currentlabel
% ==> need to set \@currentlabel manually before calling label

% cleveref.sty
% \item looks unchanged (no mention)
% \refstepcounter is changed!
% \def\refstepcounter{%
% - calls \refstepcounter@optarg or \refstepcounter@noarg
% - basically the same flow, both call the old \refstepcounter
% - same problem: call to \refstepcounter was skipped; 
% we have set \@currentlabel manually, and need to do similarly for \cref@currentlabel

\makeatletter
\newcommand{\labelsuffix}[2]{%
    % this is required for hyperref:
    \addtocounter{\@listctr}{-1}%
    \refstepcounter{\@listctr}%

    % fix for \ref:
    \edef\@currentlabel{\@currentlabel#2}%

    % fix for \cref (if you need to, load hyperref *before* cleveref):
    % TODO: this ignores prefixes and counter aliases!
    % TODO: to fix, copy/patch additional parts from \refstepcounter@noarg (from cleveref.sty)
    \global\def\cref@currentlabel{[\@listctr][\arabic{\@listctr}][]\@currentlabel}%

    % fix without enumitem
    \expandafter\edef\csname the\@listctr\endcsname{\csname the\@listctr\endcsname#2}%

    % fix with enumitem: label\@listctr does not contain the\@listctr, but c@\@listctr directly.
    % so we patch; silently fails when enumitem is not loaded.
    % https://tex.stackexchange.com/questions/340620/
    \expandafter\def\expandafter\label@listctr\expandafter{\csname label\@listctr\endcsname}%
    \expandafter\def\expandafter\c@@listctr\expandafter{\csname c@\@listctr\endcsname}%

    \expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter%
    \xpatchcmd\expandafter\expandafter\expandafter%
    \label@listctr\expandafter\expandafter\expandafter%
        {\expandafter\expandafter\expandafter%
            {\expandafter%
                \c@@listctr\expandafter%
            }\expandafter%
        }\expandafter%
        {\expandafter%
            {\c@@listctr}#2%
        }{}{err}%

    \csname label\@listctr\endcsname%
    \label{#1}%
}
\makeatother

\begin{document}
    \begin{enumerate}
    \item\label{item:A}

    \item\label{item:Aa}
    \begin{enumerate}

        \item\label{item:B}

        \item[\labelsuffix{item:C}{'}]

        \item[\labelsuffix{item:D}{*}]

        \item\label{item:E}
    \end{enumerate}

    \item[\labelsuffix{item:F}{'}]

    \item\label{item:G}

    \item[\labelsuffix{item:H}{*}]

    \end{enumerate}

    items \ref{item:A}, \ref{item:Aa}, \ref{item:B}, \ref{item:C}, \ref{item:D}, \ref{item:E}, \ref{item:F}, \ref{item:G} and \ref{item:H}

    \cref{item:A,,item:Aa,,item:B,,item:C,,item:D,,item:E,,item:F,,item:G,,item:H}

    (cref does not sort items in different levels correctly -- I have filed that as a bug with the developer.)

\end{document}

相关内容