使用\newcounter{counter}[resetwith]
,我可以定义一个计数器,当发生变化时自动重置resetwith
。
然而,似乎 的\thecounter
定义只是显示 的值counter
,而没有表明resetwith
它与 的值有关。
这让我感到非常困惑,因为我习惯于\newtheorem{counter}{Display name}[resetwith]
“正确地”定义事物以获得一致的定理编号。
当然我可以\thecounter
在具体的案例中手动定义。
但是,我正在编写一个文件,该文件将在几种情况下被包含和使用:哪个计数器是resetwith
或者是否会自动重置取决于上下文,所以我正在寻找一个\newcounter
能够以通用方式满足我需求的“升级”版本。
有没有“好”的方法来做到这一点?
目前我正在\edef\@temp{\noexpand\newcounter{question}\resetlevel}\@temp
使用\resetlevel
一个宏,该宏可以扩展为无内容或方括号之间的计数器名称。
为了让事情变得更有趣,我还使用了 hyperref,它似乎可以用计数器定义做一些事情。
答案1
让我们看一下 ; 的定义,以便我将使用和 来\setcounter
举例。\newcounter{foo}
\newcounter{foo}[baz]
% latex.ltx, line 2157:
\def\newcounter#1{%
\expandafter\@ifdefinable \csname c@#1\endcsname
{\@definecounter{#1}}%
\@ifnextchar[{\@newctr{#1}}{}}
为了查看计数器是否尚未定义,LaTeX 会检查 ,\c@foo
因为正如我们将看到的,相关计数寄存器的名称是通过在计数器名称前加上 来获得的c@
。如果成功,\@definecounter{foo}
则执行:
\def\@definecounter#1{\expandafter\newcount\csname c@#1\endcsname
\setcounter{#1}\z@
\global\expandafter\let\csname cl@#1\endcsname\@empty
\@addtoreset{#1}{@ckpt}%
\global\expandafter\let\csname p@#1\endcsname\@empty
\expandafter
\gdef\csname the#1\expandafter\endcsname\expandafter
{\expandafter\@arabic\csname c@#1\endcsname}}
计数寄存器被分配,\newcount\c@foo
并且为了安全起见,\setcounter{foo}{0}
被发出。
\cl@foo
然后定义一个全局宏并将其初始化为空;它将保存在foo
步进时应重置的计数器的名称。然后将计数器名称添加到操作\cl@ckpt
期间使用的虚拟计数器的重置列表中。\include
定义全局宏\p@foo
(打印计数器值时的“前缀”)并初始化为空。
最后,\thefoo
基本上是全局定义为默认表示\arabic{foo}
。
如果还跟着可选参数,比如的情况\newcounter{foo}[baz]
,也会\@newctr{foo}[baz]
执行。
% latex.ltx, line 2162:
\def\@newctr#1[#2]{%
\@ifundefined{c@#2}{\@nocounterr{#2}}{\@addtoreset{#1}{#2}}}
如果\c@baz
不存在,则会引发错误,因为我们试图将foo
不存在的计数器添加到重置列表中。否则\@addtoreset{foo}{baz}
执行,这实际上会附加foo
到重置列表中baz
。
注意不\thefoo
如果出现可选参数,就会发生重新定义。
如果您希望的表示foo
也依赖于baz
,则需要明确地告诉它;例如
\newcounter{foo}[baz]
\renewcomand{\thefoo}{\thebaz.\arabic{foo}}
LaTeX 并不具备千里眼的能力,而且也没有声称自己具备这种能力。
为什么\newtheorem{foo}{Foo}[baz]
实际执行
\renewcommand{\thefoo}{\thebaz.\arabic{foo}}
而不是?因为大多数情况下人们想要定理表明了定理所服从的计数器的值。最常见的调用是
\newtheorem{theorem}{Theorem}[section]
人们普遍认为定理的编号方式如下<section>.<theorem>
。
我认为你不应该做那么复杂的事,只要记住发出适当的\renewcommand{\thefoo}{...}
指令就足够了。
为了实现最大的灵活性,使用键值系统可能有一个更好的定义:
\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\NewDocumentCommand{\NewCounter}{mO{}}
{
\group_begin: % to not clutter the values
\keys_set:nn { ysalmon/newcounter } { #2 }
\tl_if_empty:NTF \l_ysalmon_newcounter_within_tl
{
\newcounter{#1}
\cs_gset:cpx { the#1 }
{
\exp_not:c { \l_ysalmon_newcounter_repr_tl }
{ #1 }
}
}
{
\newcounter{#1}[\l_ysalmon_newcounter_within_tl]
\cs_gset:cpx { the#1 }
{
\bool_if:NT \l_ysalmon_newcounter_prefix_bool
{
\exp_not:c { the \l_ysalmon_newcounter_within_tl }
\exp_not:V \l_ysalmon_newcounter_sep_tl
}
\exp_not:c { \l_ysalmon_newcounter_repr_tl }
{ #1 }
}
}
\clist_map_inline:Nn \l_ysalmon_newcounter_also_clist
{
\counterwithin*{#1}{##1}
}
\tl_gset_eq:cN { p@#1 } \l_ysalmon_newcounter_prefix_tl
\group_end:
}
\keys_define:nn { ysalmon/newcounter }
{
within .tl_set:N = \l_ysalmon_newcounter_within_tl,
within* .code:n = \keys_set:nn { ysalmon/newcounter } { whithin=#1 }
\bool_set_false:N \l_ysalmon_newcounter_prefix_bool,
also .clist_set:N = \l_ysalmon_newcounter_also_clist,
sep .tl_set:N = \l_ysalmon_newcounter_sep_tl,
sep .initial:n = .,
repr .tl_set:N = \l_ysalmon_newcounter_repr_tl,
repr .initial:n = arabic,
prefix .tl_set:N = \l_ysalmon_newcounter_prefix_tl,
}
\bool_new:N \l_ysalmon_newcounter_prefix_bool
\bool_set_true:N \l_ysalmon_newcounter_prefix_bool
\ExplSyntaxOff
\NewCounter{fooA}
\show\thefooA
\NewCounter{fooB}[
repr=Roman,
]
\show\thefooB
\NewCounter{fooC}[
repr=roman,
sep=-,
within=fooA
]
\show\thefooC
\NewCounter{fooD}[
within*=fooB,
also={section,subsection},
]
\show\thefooD
我们定义四个计数器并展示它们的表示。键
within
指定当前计数器应从属于的计数器,并\the<counter>
以该计数器的表示形式作为前缀within*
相同,但没有在表示中添加前缀also
指定当前计数器应从属于的可选计数器列表(不添加表示)repr
arabic
应为、roman
、Roman
、alph
、之一(或您定义的其他计数器表示Alph
)fnsymbol
sep
是“内部”计数器表示和当前计数器表示之间的分隔符prefix
套\p@<counter>
这是控制台输出:
> \thefooA=\long macro:
->\arabic {fooA}.
l.51 \show\thefooA
?
> \thefooB=\long macro:
->\Roman {fooB}.
l.57 \show\thefooB
?
> \thefooC=\long macro:
->\thefooA -\roman {fooC}.
l.65 \show\thefooC
?
> \thefooD=\long macro:
->\arabic {fooD}.
l.71 \show\thefooD
答案2
\thecounter
我认为我找到了一个解决方案,可以让我使用以下界面进行适当的设置
\edef\@temp{\noexpand\setthe{question}\resetlevel}\@temp
但是我还不知道这与 hyperref 有何关系。
的定义\setthe
是
\def\setthe#1{\@ifnextchar[{\setthei{#1}}{}}
\def\setthei#1[#2]{%
\expandafter\let\expandafter\@temp\csname the#1\endcsname
\toks@\expandafter{\@temp}%
\expandafter\xdef\csname the#1\endcsname{\expandafter\noexpand\csname the#2\endcsname\@thmcountersep\the\toks@}}
难点在于获取的当前含义以\csname the#1\endcsname
对其进行升级,但又不完全扩展它(因为它会在文档开头扩展为 0,并且不会使用计数器值进行更新)。
\@thmcountersep
在我的定义中是因为我想显示像定理数这样的值;它可以被替换为例如一个点。