提前提醒一下:这篇文章会很长。过去几天,我花了不少时间试图解决以下问题:我正在排版数学讲义,并使用该ntheorem
包定义各种定理类环境,经常遇到以下形式的情况
\begin{mythm}\begin{enumerate}
\item\label{a}
\item\label{b}
\end{enumerate}\end{mythm}
理想情况下输入(就我而言,因为我使用该cleveref
包)
\cref{b}
应该产生形式为“定理 3 (b)”的东西(即包括标签所在定理环境的名称和编号)。
事实证明这相当困难,而且记录也不是很完善。主要问题是涉及两个计数器(enumi
我们称之为定理计数器thmcnt
),以及定理名称。
我的第一次尝试是,使用该etoolbox
包,侵入环境mythm
,每次启动时创建一个新的宏,保存定理名称和编号,并可以使用命令进行格式化(和引用)cleveref
,例如
\BeforeBeginEnvironment{mythm}{
\crefname{\labelcode}{Theorem~\thethmcnt}
\edef\Label#1{\noexpand\label[\labelcode]{#1}}
}
此外,\labelcode
对于此环境调用还应包含一些独特的内容,例如
\edef\labelcode{ctr@thm@\roman{chapter}@\roman{section}@\arabic{thmcnt}
而且,我们前环境的执行,我们需要记住将计数器增加一(完成后将其放回原位),否则保存的定理计数器\crefname
将会偏离一。
因此,理想情况下,在上面的例子中,我们可以写
\begin{mythm}\begin{enumerate}
\item\Label{a}
\item\Label{b}
\end{enumerate}\end{mythm}
并且\cref{b}
应该能做我们想要它做的事情。不幸的是,据我所知,cleveref
忽略了 之后的所有\crefname
命令\begin{document}
,所以事情没那么简单。
我当时尝试将所有格式信息写入一个文件,然后让 LaTeX 下次读取它,这样在编译文档两次后,所有链接信息都可以访问。例如
\newread\foo
\immediate\openin\foo=\filename
\ifeof\foo\relax\else\input\filename\relax\fi
\immediate\closein\foo
\newwrite\blah
\immediate\openout\blah=\filename
\AtEndDocument{\immediate\write\blah{\string\endinput}\relax
\immediate\closeout\blah}
然后将上面的相应行替换为
\immediate\write\blah{\string\crefname{\labelcode}{Theorem~\thethmcnt}{}}
这似乎有效,尽管在我看来,对于一个(我认为是)相对常见的问题来说,这似乎相当复杂。如果有人(仍在阅读;))并且知道一个更简单的解决方案,我会很高兴并且有兴趣知道!
不过,还有一个小缺点。当cleveref
与一起使用时hyperref
,它还会将所有引用变成可点击的链接。然而,在当前解决方案中,只有“(b)”变成了超引用。显而易见的方法是使用cleveref
s\crefformat
命令,但对于此目的,它的语法不切实际,也就是说,我们必须编写类似这样的代码
\crefformat{\labelcode}{#2Theorem~\thethmcnt~#1#3}
在我们的\BeforeBeginEnvironment
区块中,但这当然有和之前一样的问题,\thethmcnt
产生了我们所处的定理的数量引用时而不是我们所引用的定理。
因此我尝试像上面一样将其简单地写入我们的文件中
\immediate\write\blah{\noexpand\crefformat{\labelcode}{#2Theorem~\thethmcnt~#1#3}}
但不幸的是,这不起作用,这就是我陷入困境的地方:将其写入文件不起作用,因为会#
被替换为##
,这会导致 LaTeX 读取文件时出错。如果在重新运行 LaTeX 之前在输出文件中手动替换,它实际上会起作用,##
但这#
不是一个非常令人满意的解决方案。我想可以编写一个外部脚本并在每次编译时将其与 LaTeX 一起调用,或者使用命令\write18
或类似的东西,但这不利于协作或跨平台实现,所以有人知道 LaTeX 内部解决方案吗?
我已经尝试过更改 catcode#
或尝试通过\char
类似命令访问它,但这些都不起作用,总是导致类似
! Illegal parameter number in definition of \@groupformat.
有人有什么意见或想法吗?感谢您读到这里 :)
编辑:此功能现已包含在coolthms
包中,可在氯化三乙胺。
答案1
您可以使用以下方法将哈希字符写入#
外部文件。我用虚拟宏替换了一些宏,以保持代码简洁。
\documentclass{article}
\begingroup
\catcode`\#=12
\gdef\hashchar{#}%
\endgroup
% Alternative
\edef\althashchar{\string#}%
% dummy macros
\def\labelcode{LABEL}
\def\thethmcnt{thethmcnt}
\def\crefformat#1#2{}
\chardef\blah=1\relax% write to .aux file
\begin{document}
% Works
\immediate\write\blah{\noexpand\crefformat{\labelcode}%
{\hashchar2Theorem\string~\thethmcnt\string~\hashchar1\hashchar3}}
% Works as well
\immediate\write\blah{\noexpand\crefformat{\labelcode}%
{\althashchar2Theorem\string~\thethmcnt\string~\althashchar1\althashchar3}}
% Works too
\immediate\write\blah{\noexpand\crefformat{\labelcode}%
{\string#2Theorem\string~\thethmcnt\string~\string#1\string#3}}
% Now inside a macro:
\def\mywritemacro{%
% Works
\immediate\write\blah{\noexpand\crefformat{\labelcode}%
{\hashchar2Theorem\string~\thethmcnt\string~\hashchar1\hashchar3}}
% Works as well
\immediate\write\blah{\noexpand\crefformat{\labelcode}%
{\althashchar2Theorem\string~\thethmcnt\string~\althashchar1\althashchar3}}
% Works too
\immediate\write\blah{\noexpand\crefformat{\labelcode}%
{\string##2Theorem\string~\thethmcnt\string~\string##1\string##3}}
}
\mywritemacro
\end{document}
结果文件(此处.aux
):
\relax
\crefformat {LABEL}{#2Theorem~thethmcnt~#1#3}
\crefformat {LABEL}{#2Theorem~thethmcnt~#1#3}
\crefformat {LABEL}{#2Theorem~thethmcnt~#1#3}
\crefformat {LABEL}{#2Theorem~thethmcnt~#1#3}
\crefformat {LABEL}{#2Theorem~thethmcnt~#1#3}
\crefformat {LABEL}{#2Theorem~thethmcnt~#1#3}
答案2
无论如何,enumerate
当您真正想要一个subtheorem
带有自己的计数器的环境时,请远离使用该环境。
\documentclass{article}
\usepackage{ntheorem}
%\usepackage{amsthm}
\usepackage{thmtools}
\usepackage{enumitem}
\usepackage{hyperref}
\usepackage{cleveref}
\declaretheorem[name=My Theorem,
]{mythm}
% yes, this boilerplate could be automated: do it for every theorem.
% in theory, wasting counters could be avoided by use of counter aliasing,
% but that would require patching of enumitem.
\newlist{submythm}{enumerate}{1}
\crefname{submythmi}{My Theorem}{My Theorems}
\setlist[submythm]{label=(\alph*),%
ref=\themythm (\alph*)}
% dynamically turn into the propery submythm, sublemma, whatnot.
\makeatletter
\newenvironment{subs}{%
\edef\subname{sub\thmt@envname}%
\expandafter\begin\expandafter{\subname}% enumitem requires explicit begin
}{%
\expandafter\end\expandafter{\subname}%
}
\makeatother
\begin{document}
\begin{mythm}\begin{subs}
\item\label{a} Hello!
\item\label{b} Hello yourself!
\end{subs}\end{mythm}
\begin{enumerate}
\item bim!\label{c}
\end{enumerate}
Those were \ref{a} and \ref{b} and \ref{c}
Those were \cref{a} and \cref{b} and \cref{c}
\end{document}
编辑:这种变体允许\ref
,但它会在下一个定理之前产生 1 (a) ,这可能不是你想要的。(可能可以通过谨慎使用 来修复\p@foo
,但我认为如果你有定理 1 A...b...推论1 A...b...定理 1 的证明...推论 1 的证明...:不清楚在哪里可以找到对(A)。
答案3
需要说明的是,虽然\crefname
必须在前言中使用,但较低级别的\crefformat
et al. 命令即使在 之后也能正常工作。因此,如果您使用而不是 ,\begin{document}
您的原始解决方案可能有效。\crefformat
\crefname
另一方面,Ulrich 的解决方案似乎更加优雅。