履歴書*リスト内の項目を参照すると、追加のテキストが表示されます

履歴書*リスト内の項目を参照すると、追加のテキストが表示されます

私は自分の生活を少し自動化したいと思っていましたが、enumitemパッケージには、以前のリストに関してカウントを再開できるカスタム リストを作成する方法があることを発見しました。それがなければ、enumiと を使用してカウンターを明示的に操作する必要があります\setcounterが、正しくカウントできないことがよくあるので、面倒な作業になる可能性があります。 :-) とにかく、 をenumitem使用すると、 を使用したときの連続カウントが機能しますresume*。ただし、そうすることで、cleverref による参照が壊れてしまいます。そのリストから項目を参照すると、追加のresume,,テキストが印刷されます。これをどうやって取り除くことができますか? プリアンブルで何を間違えているのでしょうか、それともこれは何らかのバグでしょうか?

問題を描写した画像

\documentclass[a4paper,english]{article}

\usepackage{enumitem}
\usepackage{amssymb}
\usepackage{cleveref}

% defining theorem environments for their specialized versions
\makeatletter
\newenvironment{base@thm}[3]%
{$\;$\linebreak\noindent\mbox{$\triangleright\;\textsc{\textbf{\large #1} #2 (#3).}$}}
{\hfill$\;$\linebreak}
\makeatother

% lemma environments take a label and a name
\newcounter{lemma}
\crefname{lemma}{lemma}{lemmata}
\makeatletter
\newenvironment{lemma}[2]%
{\refstepcounter{lemma}\begin{base@thm}{Lemma}{\thelemma}{#2}\label[lemma]{lemma:#1}\def\@currentlabel{#2}\label{t@lemma:#1}}
{\end{base@thm}\noindent}
\makeatother
% referencing a lemma
\newcommand{\lemmaref}[1]{\Cref{lemma:#1}~(\ref{t@lemma:#1})}
\newcommand{\lemref}[1]{\Cref{lemma:#1}}

\newlist{passumptionslist}{enumerate}{1}
\setlist[passumptionslist]{label=(\alph*)}

\crefalias{asm}{passumptionslisti}
\crefalias{goal}{enumi}
\crefname{asm}{assumption}{assumptions}
\crefname{goal}{goal}{goals}
\newenvironment{assumptions}
    {\begin{passumptionslist}[label=(\alph*)]
    }
    {
    \end{passumptionslist}
  }
\newenvironment{goals}
    {\begin{enumerate}[label=(\roman*)]
    }
    {
  \end{enumerate}
  }

\newcommand{\IH}[1][]{I{\kern-1.5pt}H#1}
\newenvironment{passumptions}[1]
  {%
  \begin{passumptionslist}[resume*,label={$\left(#1_{\arabic*}\right)$}]%
  }
  {%
  \end{passumptionslist}%
  }
% individual cases
\newcommand{\asm}[2]{\item\label[asm]{asm:#1} {$#2$}}
\newcommand{\goal}[2]{\item\label[goal]{goal:#1} {$#2$}}
\newcommand{\asmref}[1]{\Cref{asm:#1}}
\newcommand{\goalref}[1]{\Cref{goal:#1}}

\newenvironment{proof}[1][\textbf{Proof}]%
{\restartlist{passumptionslist}\par\noindent\ignorespaces\mbox{\textsc{#1}.}$\;$\\\noindent}
{$\;$\hfill$\square$\\\ignorespacesafterend}


\begin{document}

\begin{lemma}{lemma}{Some Lemma Name}
  If
  \begin{assumptions}
    \asm{lemma}{a}
  \end{assumptions}
  then
  \begin{goals}
    \goal{lemma}{b}
  \end{goals}
\end{lemma}
\begin{proof}
  Unfold \goalref{lemma} and introduce the assumptions.
  \begin{passumptions}{H}
    \asm{lemma0}{v\Downarrow b}
  \end{passumptions}
  What is left to show is:
  \begin{goals}
    \goal{lemma0}{c}
  \end{goals}

  From \asmref{lemma0}, get:
  \begin{passumptions}{H}
    \asm{lemma1}{a=v}
  \end{passumptions}

  Rewrite \asmref{lemma} and \asmref{lemma0} using \asmref{lemma1}:
  \begin{passumptions}{H}
    \asm{lemmap}{v=c}
    \asm{lemma0p}{a\Downarrow b}
  \end{passumptions}

  Use \asmref{lemma0p}:
  \begin{passumptions}{H}
    \asm{lemma2}{a=b}
  \end{passumptions}
  Finally, rewrite \goalref{lemma0} via \asmref{lemma1}.
  Then, \asmref{lemma2} solves it.
\end{proof}


\end{document}

答え1

この回答は、なぜresume,,印刷されるのかについて説明します。

基本的に、互いに衝突する 2 つの事柄があります。

[resume*]先行詞がない場合、リストは壊れます

この動作は、次の単純な MWE で確認できます。

\documentclass{article}

\usepackage{enumitem}

\begin{document}

\begin{enumerate}[resume*]
        \item Test
\end{enumerate}
\end{document}

なぜこれが機能しなくなるのでしょうか? を有効にするにはresume*enumitemコードは、リストの前回の発行から次回のリストの呼び出しまでリスト構成を渡す必要があります。これは、\enit@resumekeys@<list>(ここで<list>はリストの名前、つまりenumerateまたは に置き換えられますpassumptionslist) を (基本的に) リストにオプションの引数として渡されたものに設定することで行われますenumitemこれはリストの最後に実行されますなので、\end{enumerate}上の例で に遭遇すると、いいえデフォルト値が作成されます。そのため、リストが最初に呼び出される前に、マクロ\enit@resumekeys@<list>未定義

ここで、resume*がリストに渡されると、enumitemはリストへの最後の呼び出しから構成をロードしようとし、 を調べます\enit@resumekeys@<list>。特に、これは 構文を使用して行われる\csname ... \endcsnameため、\enit@resumekeys@<list>が未定義の場合でもエラー (未定義マクロ エラー) は発生しません。 を使用して\csname ... \endcsname未定義マクロにアクセスすると、常に が返されます\relax

現在の呼び出しのキーが以前に指定されたキーを上書きできるようにするには、enumitem 先頭に付加するこれをオプション引数に追加します。したがって、resume*MWE で を処理した後、有効なオプション引数は になります\relax,resume

しかし、オプションのリストを処理するには、enumitemカンマで区切られたリストを一度に1項目ずつ処理する必要があります。これは標準的なトリックを使用して行われます。マクロを定義するには、

\def\enitkv@do#1,{%
    \ifx\relax#1\empty\else
    \enitkv@split#1==\relax %% This processes the key, ignore it for now
    \expandafter\enitkv@do\fi}

マクロはカンマで区切られます。そのため、カンマで区切られたリストを入力すると、次の文字列まで次の文字列を処理し,、その文字列 (およびカンマ) を破棄して、次の引数に対して同じ処理を繰り返します。

このマクロはどうやって停止するかを知るのでしょうか? に遭遇すると停止します\relax。そして実際には、このマクロは次のように呼び出されます。

\enitkv@do#2,\relax,

はオプションの引数のリストです#2。これで問題が分かります。resume*先行詞なしで使用する場合上記のマクロが呼び出されると、

\enitkv@do\relax,resume,\relax,

最初の\relaxヒットでマクロは終了します。つまり、残っているのはぶら下がった部分です。

resume,\relax,

これらはマクロの引数ではないので、テキストとして処理されます。そして、\relax何もしないので、印刷すると次のようになります。

resume,,

あなたが見るもの。

グループ

しかし、ちょっと待ってください、あなたは環境もassumptionsで定義しpassumptionslist、それを前にpassumptionsコードの最初のものを呼び出します。これは、実際には先行項が存在するはずであることを意味しませんか?

これについては、次の 2 つの MWE を比較する必要があります。

MWE 1 (グループなし)

\documentclass{article}

\usepackage{enumitem}

\begin{document}

\begin{enumerate}
    \item Test
\end{enumerate}

\begin{enumerate}[resume*]
        \item Test
\end{enumerate}
\end{document}

MWE 2(グループ)

\documentclass{article}

\usepackage{enumitem}

\begin{document}

\bgroup
\begin{enumerate}
    \item Test
\end{enumerate}
\egroup

\begin{enumerate}[resume*]
        \item Test
\end{enumerate}
\end{document}

MWE 1 は適切に動作しますが、MWE 2 は不適切に動作します。では、ここで何が起こっているのでしょうか?

以下は の機能ですenumitemが、良いアイデアかどうかはわかりません。 現在のオプションをリストに保存する場合、 はenumitem4 つの異なるケースで異なる動作をします。

  1. または でseriesはないオプションがある場合、コードは を使用して、現在のカウンター値と現在のオプション引数の両方を保存します。resumeresume*\global\def
  2. またはでもseriesあるオプションがある場合、コードは を介し​​てカウンターのみを保存しますが、現在のオプション引数は保存しません。resumeresume*\global\def
  3. が存在しないseriesが、 が存在する場合resume*、コードは を介し​​てカウンタのみを保存します\global\defが、オプションの引数は保存しません。
  4. seriesまたは でもない単純なリストがある場合resume*(ただし、奇妙なことに、resumeここでは問題ありません)、コードは を使用してカウンターとオプションの引数の両方を保存します\def

これにより、いくつかの興味深い副作用が発生します。

  • \begin{enumerate}[resume]...\end{enumerate}を で囲むと\newenvironment、4 番目のケースがトリガーされます。ただし、環境がグループ化されているため、 は\def環境外に伝播しません。これが、で観察したように番号付けが間違っている理由を説明しています。このコメント
  • 元のコードを参考にすると、 を介して定義された環境\begin{passumptionslist}...\end{passumptionslist}でラップされているため、 を呼び出すと、パッケージは の現在のカウンターとリスト オプションを保存しようとしますが、ケース 4 の場合、これらはローカル定義を介して作成されるため、 に達するとすぐに失われます。assumptions\newenvironment\begin{assumptions}...\end{assumptions}\end{passumptionslist}\end{assumptions}

上記で特定された最初の問題は、少し変わったエッジケースです。 の通常の使用では、以前に使用されたことのないリストをenumitem呼び出すべきではありません[resume*]。とはいえ、このエッジケースは、 のリストの終わりを区別するために別のトークンを使用するだけで修正できます\def\enitkv@do#1,

2 番目の問題は、実際には文書化されています (ただし、やや不十分です)。enumitem のドキュメントの現在のバージョンでは、resumeローカルであることがセクション 3.3 で簡単に説明されています。ただし、この奇妙な相互作用はresume*明確に文書化されていません (間違いなく文書化される必要があります)。

答え2

さまざまな環境にラップされる基盤の使用は不要だと思います。すべてをspassumptionslistとして定義する方がよいでしょう。\newlist

%%%% UNTIL ANNOUNCED, SAME AS YOUR CODE %%%%
\documentclass[a4paper,english]{article}

\usepackage{enumitem}
\usepackage{amssymb}
\usepackage{cleveref}

% defining theorem environments for their specialized versions
\makeatletter
\newenvironment{base@thm}[3]%
{$\;$\linebreak\noindent\mbox{$\triangleright\;\textsc{\textbf{\large #1} #2 (#3).}$}}
{\hfill$\;$\linebreak}
\makeatother

% lemma environments take a label and a name
\newcounter{lemma}
\crefname{lemma}{lemma}{lemmata}
\makeatletter
\newenvironment{lemma}[2]%
{\refstepcounter{lemma}\begin{base@thm}{Lemma}{\thelemma}{#2}\label[lemma]{lemma:#1}\def\@currentlabel{#2}\label{t@lemma:#1}}
{\end{base@thm}\noindent}
\makeatother
% referencing a lemma
\newcommand{\lemmaref}[1]{\Cref{lemma:#1}~(\ref{t@lemma:#1})}
\newcommand{\lemref}[1]{\Cref{lemma:#1}}

%%%% CHANGES BELOW %%%%
% 1. Removed passumptslist
% 2. Removed your crefaliases

% The crefname are kept
\crefname{asm}{assumption}{assumptions}
\crefname{goal}{goal}{goals}

% 3. Defined assumptions, passumptions, and goals directly as new lists
\newlist{assumptions}{enumerate}{1}
\newlist{passumptions}{enumerate}{1}
\newlist{goals}{enumerate}{1}

% 3. (cont'd) and set their formatting directly
\setlist[assumptions]{label=(\alph*)}
\setlist[goals]{label=(\roman*)}

% 4. Set the format for passumptions; define macro to change the label.
\makeatletter
\newcommand*\setPassLabel[1]{\gdef\@passLabel{#1}}
\setlist[passumptions]{resume,label={$(\@passLabel_{\arabic*})$}}
\makeatother

%%%%% BELOW AGAIN UNCHANGED, EXCEPT MARKED LINES %%%%%%%
\newcommand{\IH}[1][]{I{\kern-1.5pt}H#1}
% individual cases
\newcommand{\asm}[2]{\item\label[asm]{asm:#1} {$#2$}}
\newcommand{\goal}[2]{\item\label[goal]{goal:#1} {$#2$}}
\newcommand{\asmref}[1]{\Cref{asm:#1}}
\newcommand{\goalref}[1]{\Cref{goal:#1}}

%%%% Make the list that is restarted the passumptions list, and not passumptionslist.
\newenvironment{proof}[1][\textbf{Proof}]%
{\restartlist{passumptions}\par\noindent\ignorespaces\mbox{\textsc{#1}.}$\;$\\\noindent}
{$\;$\hfill$\square$\\\ignorespacesafterend}


\begin{document}

\begin{lemma}{lemma}{Some Lemma Name}
  If
  \begin{assumptions}
    \asm{lemma}{a}
  \end{assumptions}
  then
  \begin{goals}
    \goal{lemma}{b}
  \end{goals}
\end{lemma}
\begin{proof}
        \setPassLabel{H}   %%% Set the label for the passumptions. 
  Unfold \goalref{lemma} and introduce the assumptions.
  \begin{passumptions}   %%% No more mandatory argument (Ditto below)
    \asm{lemma0}{v\Downarrow b}
  \end{passumptions}
  What is left to show is:
  \begin{goals}
    \goal{lemma0}{c}
  \end{goals}

  From \asmref{lemma0}, get:
  \begin{passumptions}
    \asm{lemma1}{a=v}
  \end{passumptions}

  Rewrite \asmref{lemma} and \asmref{lemma0} using \asmref{lemma1}:
  \begin{passumptions}
    \asm{lemmap}{v=c}
    \asm{lemma0p}{a\Downarrow b}
  \end{passumptions}

  Use \Cref{asm:lemma0p}; \asmref{lemma0p}:
  \begin{passumptions}
    \asm{lemma2}{a=b}
  \end{passumptions}
  Finally, rewrite \goalref{lemma0} via \asmref{lemma1}.
  Then, \asmref{lemma2} solves it.
\end{proof}

\begin{proof}
        \setPassLabel{K}
        \begin{passumptions}
                \asm{lemma3}{c}
        \end{passumptions}
\end{proof}

\end{document}

ここに画像の説明を入力してください

説明

  1. すべてのリスト環境を、リスト をラップする\newlistsとして定義するのではなく、として直接定義する方がよいと思います。 その理由は、この方法により、オプションの引数を介してオンザフライでリストを変更したり、 をグローバルに使用したりして、インターフェイスを使用してリストを変更できる一貫性が確保され、リストの使用方法の柔軟性が向上するためです。\newenvironmentspassumptionslistenumitem\setlist

  2. 私の知る限り、 は\crefaliasコードに影響を与えません。特に で潜在的に問題を引き起こすことがわかっているため、これらを削除しました。 問題ないのは、 と を使用するときはいつでもhyperrefオプション引数を使用しているため、オプション引数から型情報を取得できるからです。\label\asm\goal\Cref

  3. \newlistsと を使用すると、\setlist書式設定の構成がより透明になると思います。

  4. これは、作業が必要な唯一の部分です。元のコードでは、基礎となるリストの をpassumptions設定するために使用する必須引数を使用して を定義しました。私はこの設定に同意しません。まず、コードから、が「証明ごとに」使用されていることは明らかなので、理想的には、同じ証明内の連続するものは同じラベル タイプを持つ必要があります。(項目に H1、H2、次に C3、D4、H5 と番号が付けられると奇妙に見えると思います。) それを念頭に置いて、ラベルをグローバルに変更する方法があった方がよいでしょう (後続のすべてのリストに対して)。ちなみに、そうすることで、としてコーディングすることも可能になります。この方法で作成されたリストは必須引数を取ることができないためです。labelpassumptionspassumptionspassumptionspassumptions\newlist

    この設計哲学に基づいて、 のラベル記号/文字を設定するコマンドを作成しpassumptions、その記号を使用してこのリストの書式を設定します。また、resumeこのリストは各証明の開始時にリセットされるまで再開されることを意図しているため、キーも追加しました。

関連情報