2

With \newcounter{counter}[resetwith], I can define a counter to be automatically reset when resetwith changes.

However, it seems that \thecounter is defined to just display the value of counter, and does not indicate the value of resetwith to which it is relative.

This has been dumbfounding to me because I am used to \newtheorem{counter}{Display name}[resetwith] to "properly" define things to get a coherent numbering of theorems.

Of course I can manually define \thecounter in a concrete case.

However I am writing a file that is to be included and used in several circumstances : which counter is resetwith or whether there will be an automatic reset at all depends on the context, so I am looking for an "upgraded" version of \newcounter that does what I need in a generic way.

Is there a "good" way to do this ?

Currently I am doing \edef\@temp{\noexpand\newcounter{question}\resetlevel}\@temp with \resetlevel a macro that expands to either nothing or the name of a counter between square brackets.

To spice things up, I am also using hyperref, and it seems to do some things with counter definitions.

  • 1
    \renewcommand{\thecounter}{\theresetwith.\arabic{counter}} – John Kormylo Oct 13 '18 at 12:55
1

Let's look at the definition of \setcounter; for the examples I'll use \newcounter{foo} and \newcounter{foo}[baz].

% latex.ltx, line 2157:
\def\newcounter#1{%
  \expandafter\@ifdefinable \csname c@#1\endcsname
    {\@definecounter{#1}}%
  \@ifnextchar[{\@newctr{#1}}{}}

In order to see whether the counter has not yet been defined, LaTeX checks for \c@foo because, as we shall see, the name of the associated count register is obtained by prefixing the counter name with c@. If this is successful, \@definecounter{foo} is executed:

\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}}

The count register is allocated with \newcount\c@foo and, for safety, \setcounter{foo}{0} is issued.

Then a global macro \cl@foo is defined and initialized to be empty; it will hold the names of counters that should be reset when foo is stepped. Then the counter name is added to \cl@ckpt, the reset list of a dummy counter that's used during \include operations.

The global macro \p@foo is defined (the “prefix” when the counter value is printed) and initialized to empty.

Finally, \thefoo is globally defined to the default representation, basically \arabic{foo}.

If also the optional argument follows, as in the case of \newcounter{foo}[baz], also \@newctr{foo}[baz] is executed.

% latex.ltx, line 2162:
\def\@newctr#1[#2]{%
  \@ifundefined{c@#2}{\@nocounterr{#2}}{\@addtoreset{#1}{#2}}}

If \c@baz doesn't exist, an error is raised, as we'd be attempting to add foo to the reset list of a non existent counter. Otherwise \@addtoreset{foo}{baz} is performed, which actually appends foo to the reset list for baz.

Note that no redefinition of \thefoo happens in case the optional argument appears.

If you want that the representation of foo also depend on baz, you need to tell it explicitly; for instance

\newcounter{foo}[baz]
\renewcomand{\thefoo}{\thebaz.\arabic{foo}}

LaTeX is not clairvoyant and doesn't claim to be.

Why does \newtheorem{foo}{Foo}[baz] actually perform

\renewcommand{\thefoo}{\thebaz.\arabic{foo}}

instead? Because most often people wants that theorems show the value of the counters the theorem is subordinate to. The most common call is like

\newtheorem{theorem}{Theorem}[section]

and it's widely expected that theorems are numbered like <section>.<theorem>.

I don't think you should go that complicated way when it's sufficient to remember issuing a suitable \renewcommand{\thefoo}{...} instruction.

A possibly better definition with a key-value system for maximum flexibility:

\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

We define four counters and show their representation. The keys

  1. within specifies a counter the present one should be subordinate to, with \the<counter> defined to be prefixed by the representation thereof

  2. within* is the same, but no prefix is added to the representation

  3. also specifies an optional list of counters the present one should be subordinate to (no addition to the representation)

  4. repr should be one of arabic, roman, Roman, alph, Alph, fnsymbol (or other counter representations you have defined)

  5. sep is the separator between the “within” counter representation and the current counter one

  6. prefix sets \p@<counter>

Here's the console output:

> \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
0

I think I found a solution that allows me to set the \thecounter appropriately with the following interface

\edef\@temp{\noexpand\setthe{question}\resetlevel}\@temp

However I do not know yet how this behaves with hyperref.

The definition of \setthe is

\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@}}

The difficult point was to obtain the current meaning of \csname the#1\endcsname to upgrade it, but without fully expanding it (because it would expand to 0 at the beginning of the document and not update with the counter value).

\@thmcountersep in my definition is because I want to display the values like theorem numbers ; it can be replaced with eg. a dot.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.