3

In the accepted answer to Code between \begin{environment} and \end{environment} stored in an array it's shown how to create an automatic list of theorems using expl3 syntax. However the \printtheorems command only works if it's called after the theorems.

Is there a way to call it before the theorems? I was thinking a possible way to do it would be to compile the .tex twice, the first time to get the theorems and the second one to display them (like the Table of Contents does with chapters, sections ecc..).

The code linked above and the corresponding output are:

\documentclass{article}
\usepackage{amsmath}
\usepackage{amsthm}
\usepackage{environ}
\usepackage{xparse}

\ExplSyntaxOn

\NewDocumentCommand{\newtheoremx}{momo}
{
  % * Let's duplicate the working of \newtheorem; \newtheoremx should
  %   be used for theorems that need to be listed
  % * \newtheoremx{theorem}{Theorem} will actually do
  %   \newtheorem{theorem@inner}{Theorem} (honoring the usual optional arguments)
  % * We also need a property list to store along with the theorem
  %   the one which it is subordinate to
  \IfValueTF{#2}
   {
    \newtheorem{#1@inner}[#2@inner]{#3}
    % #1 is subordinate to #2
    \prop_gput:Nnn \g_riccardo_theorems_prop { #1 } { #2 }
   }
   {
    \IfValueTF{#4}
     {
      \newtheorem{#1@inner}{#3}[#4]
     }
     {
      \newtheorem{#1@inner}{#3}
     }
    % #1 is not subordinate, store the name itself
    \prop_gput:Nnn \g_riccardo_theorems_prop { #1 } { #1 }
   }
  % define a "grabbing" environment #1 with the usual features 
  \NewEnviron{#1}[1][]
   {
    % start the inner environment (without or with optional argument)
    \tl_if_empty:nTF { ##1 }
     { \begin{#1@inner} }
     { \begin{#1@inner}[##1] }
    % save the statement number
    \tl_gset:Nx \g__riccardo_theorems_number_tl { \use:c {@currentlabel} }
    % typeset the statement
    \BODY
    % end the inner environment
    \end{#1@inner}
   % store the statement in a sequence variable, actually as
   % four arguments as shown
   \seq_gput_right:Nx \g_riccardo_theorems_seq
     {
      { #1 } % name
      { \g__riccardo_theorems_number_tl } % number
      { \exp_not:n { ##1 } } % attribution
      { \exp_not:V \BODY } } % body
    }
 }

% allocate the needed variables
\prop_new:N \g_riccardo_theorems_prop
\seq_new:N \g_riccardo_theorems_seq
\tl_new:N \g__riccardo_theorems_number_tl

% print the stored theorems
\NewDocumentCommand{\printtheorems}{}
 {
  % we need a group where nullifying the action of \label
  \group_begin:
  \cs_set_eq:NN \label \use_none:n
  % map the sequence, passing each item to the function that prints a theorem
  \seq_map_function:NN \g_riccardo_theorems_seq \riccardo_printtheorems:n
  % end the group
  \group_end:
 }

\cs_new_protected:Nn \riccardo_printtheorems:n
 {
  % just pass the argument in the form {name}{number}{attribution}{text}
  % to a four argument function
  \__riccardo_printtheorems:nnnn #1
 }
\cs_new_protected:Nn \__riccardo_printtheorems:nnnn
 {
  % redefine \the<statement>@inner to yield the stored number
  % we use the property list to use the correct counter
  % (for instance, in case of "lemma", <statement> will be "theorem"
  \cs_set:cpn { the \prop_item:Nn \g_riccardo_theorems_prop {#1} @inner } { #2 }
  \tl_if_empty:nTF { #3 }
   {
    % no attribution
    \begin{#1@inner} #4 \end{#1@inner}
   }
   {
% attribution
    \begin{#1@inner}[#3] #4 \end{#1@inner}
   }
 }

\ExplSyntaxOff

\newtheoremx{theorem}{Theorem}[section]
\newtheoremx{lemma}[theorem]{Lemma}

\begin{document}

\section{First test}

\begin{theorem}[Important]\label{thm:important}
This is a theorem about $\log_a x$.
\end{theorem}

\begin{lemma}\label{lem:whatever}
This is a lemma.
\end{lemma}

\begin{theorem}\label{thm:unimportant}
This is another theorem
\end{theorem}

\section{Second test}

\begin{theorem}\label{thm:soandso}
Again a theorem.
\end{theorem}

\section{Theorems}

\printtheorems

\end{document}

enter image description here

EDIT: MWE showing the problem

\documentclass{memoir}
\usepackage{amsmath, amsthm, hyperref, environ, xparse, showkeys}

\ExplSyntaxOn

\NewDocumentCommand{\newtheoremx}{momo}
{
  % * Let's duplicate the working of \newtheorem; \newtheoremx should
  %   be used for theorems that need to be listed
  % * \newtheoremx{theorem}{Theorem} will actually do
  %   \newtheorem{theorem@inner}{Theorem} (honoring the usual optional arguments)
  % * We also need a property list to store along with the theorem
  %   the one which it is subordinate to
  \IfValueTF{#2}
   {
    \newtheorem{#1@inner}[#2@inner]{#3}
    % #1 is subordinate to #2
    \prop_gput:Nnn \g_riccardo_theorems_prop { #1 } { #2 }
   }
   {
    \IfValueTF{#4}
     {
      \newtheorem{#1@inner}{#3}[#4]
     }
     {
      \newtheorem{#1@inner}{#3}
     }
    % #1 is not subordinate, store the name itself
    \prop_gput:Nnn \g_riccardo_theorems_prop { #1 } { #1 }
    \seq_gput_right:Nn \g_riccardo_theorems_counters_seq { #1 }
   }
  % define a "grabbing" environment #1 with the usual features 
  \NewEnviron{#1}[1][]
   {
    % start the inner environment (without or with optional argument)
    \tl_if_empty:nTF { ##1 }
     { \begin{#1@inner} }
     { \begin{#1@inner}[##1] }
    % save the statement number
    \tl_gset:Nx \g__riccardo_theorems_number_tl { \use:c {@currentlabel} }
    % typeset the statement
    \BODY
    % end the inner environment
    \end{#1@inner}
   % store the statement in a sequence variable, actually as
   % four arguments as shown
   \seq_gput_right:Nx \g_riccardo_theorems_seq
     {
      { #1 } % name
      { \g__riccardo_theorems_number_tl } % number
      { \exp_not:n { ##1 } } % attribution
      { \exp_not:V \BODY } }% body
    }
 }

% allocate the needed variables
\prop_new:N \g_riccardo_theorems_prop
\seq_new:N \g_riccardo_theorems_seq
\tl_new:N \g__riccardo_theorems_number_tl
\iow_new:N \g_riccardo_theorems_stream
\seq_new:N \g_riccardo_theorems_counters_seq

% print the stored theorems
\NewDocumentCommand{\printtheorems}{}
 {
  % we need a group where nullifying the action of \label
  \group_begin:
  \cs_set_eq:NN \label \use_none:n
  % map the sequence, passing each item to the function that prints a theorem
  \file_if_exist_input:n { \c_sys_jobname_str.thl }
  % end the group
  \group_end:
  \seq_map_inline:Nn \g_riccardo_theorems_counters_seq
   {
    \setcounter{##1@inner}{0}
   }
 }

\cs_new_protected:Nn \riccardo_theorems_save:
 {
  \seq_map_function:NN \g_riccardo_theorems_seq \__riccardo_theorems_save:n
 }
\cs_new_protected:Nn \__riccardo_theorems_save:n
 {
  \iow_now:Nn \g_riccardo_theorems_stream
   {
    \savedtheorem #1
   }
 }

\AtEndDocument
 {
  \iow_open:Nn \g_riccardo_theorems_stream { \c_sys_jobname_str.thl }
  \riccardo_theorems_save:
  \iow_close:N \g_riccardo_theorems_stream
 }

\NewDocumentCommand{\savedtheorem}{mmmm}
 {
  % redefine \the<statement>@inner to yield the stored number
  % we use the property list to use the correct counter
  % (for instance, in case of "lemma", <statement> will be "theorem"
  \cs_set:cpn { the \prop_item:Nn \g_riccardo_theorems_prop {#1} @inner } { #2 }
  \tl_if_empty:nTF { #3 }
   {
    % no attribution
    \begin{#1@inner} #4 \end{#1@inner}
   }
   {
    % attribution
    \begin{#1@inner}[#3] #4 \end{#1@inner}
   }
 }

\ExplSyntaxOff

\newtheoremx{theorem}{Theorem}[chapter]
\newtheoremx{lemma}[theorem]{Lemma}

\begin{document}

\chapter{Theorems}

\printtheorems

\clearpage
\section{First theorem}

\begin{theorem}[Important] \label{thm_1}
This is a theorem about $\log_a x$.
\end{theorem}
\clearpage

This is a link to \hyperref[thm_1]{Theorem 1}.
\end{document}
  • Where did you get the idea that \printtheorems can be used more than once? – egreg Feb 10 at 19:01
  • Oh, so the problem has nothing to do with the theorems belonging to different chapters, it's calling \printtheorems twice that's causing the issue, right? – noibe Feb 10 at 19:07
  • Just tried removing the second \printtheorems from the MWE I posted, but the theorem in the first chapter is still numbered incorrectly, so I guess different chapters are actually part of the problem. – noibe Feb 10 at 19:12
  • adding \setcounter{theorem@inner}{0} before the first theorem of every chapter seems to do the job. – noibe Feb 10 at 19:31
  • I fixed the issue with a new version, adding the resetting of the theorem numbers. – egreg Feb 10 at 20:58
6

The theorems can be saved in a sequence as before; at end document they can be written to an auxiliary file and \printtheorems inputs it (if it exists).

\documentclass{article}
\usepackage{amsmath}
\usepackage{amsthm}
\usepackage{xparse,environ}

\ExplSyntaxOn

\NewDocumentCommand{\newtheoremx}{smomo}
 {
  \IfBooleanTF{#1}
   {
    \printthm_nonumber:nn { #2 } { #4 }
   }
   {
    \printthm_number:nnnn { #2 } { #3 } { #4 } { #5 }
   }
 }

\cs_new_protected:Nn \printthm_nonumber:nn
 {
  \newtheorem*{#1@inner}{#2}
  \NewEnviron{#1}[1][]
   {
    % start the inner environment (without or with optional argument)
    \tl_if_empty:nTF { ##1 }
     { \begin{#1@inner} }
     { \begin{#1@inner}[##1] }
    % typeset the statement
    \BODY
    % end the inner environment
    \end{#1@inner}
    % store the statement in a sequence variable, actually as
    % four arguments as shown
    \seq_gput_right:Nx \g_riccardo_theorems_seq
     {
      *
      { #1 } % name
      { } % number
      \tl_if_novalue:nF { ##1 } { [\exp_not:n { ##1 }] } % attribution
      { \exp_not:V \BODY } % body
     }
   }
 }

\cs_new_protected:Nn \printthm_number:nnnn
 {
  % * Let's duplicate the working of \newtheorem; \newtheoremx should
  %   be used for theorems that need to be listed
  % * \newtheoremx{theorem}{Theorem} will actually do
  %   \newtheorem{theorem@inner}{Theorem} (honoring the usual optional arguments)
  % * We also need a property list to store along with the theorem
  %   the one which it is subordinate to
  \tl_if_novalue:nTF { #2 }
   {
    \tl_if_novalue:nTF { #4 }
     {
      \newtheorem{#1@inner}{#3}
     }
     {
      \newtheorem{#1@inner}{#3}[#4]
     }
    % #1 is not subordinate, store the name itself
    \prop_gput:Nnn \g_riccardo_theorems_prop { #1 } { #1 }
    \seq_gput_right:Nn \g_riccardo_theorems_counters_seq { #1 }
   }
   {
    \newtheorem{#1@inner}[#2@inner]{#3}
    % #1 is subordinate to #2
    \prop_gput:Nnn \g_riccardo_theorems_prop { #1 } { #2 }
   }
  % define a "grabbing" environment #1 with the usual features 
  \NewEnviron{#1}[1][]
   {
    % start the inner environment (without or with optional argument)
    \tl_if_empty:nTF { ##1 }
     { \begin{#1@inner} }
     { \begin{#1@inner}[##1] }
    % save the statement number
    \tl_gset:Nx \g__riccardo_theorems_number_tl { \use:c {@currentlabel} }
    % typeset the statement
    \BODY
    % end the inner environment
    \end{#1@inner}
    % store the statement in a sequence variable, actually as
    % four arguments as shown
    \seq_gput_right:Nx \g_riccardo_theorems_seq
     {
      { #1 } % name
      { \g__riccardo_theorems_number_tl } % number
      \IfValueT{##1}{ [\exp_not:n { ##1 }] } % attribution
      { \exp_not:V \BODY } % body
     }
   }
 }

% allocate the needed variables
\prop_new:N \g_riccardo_theorems_prop
\seq_new:N \g_riccardo_theorems_seq
\tl_new:N \g__riccardo_theorems_number_tl
\iow_new:N \g_riccardo_theorems_stream
\seq_new:N \g_riccardo_theorems_counters_seq

% print the stored theorems
\NewDocumentCommand{\printtheorems}{}
 {
  % we need a group where nullifying the action of \label
  \group_begin:
  \cs_set_eq:NN \label \use_none:n
  % map the sequence, passing each item to the function that prints a theorem
  \file_if_exist_input:n { \c_sys_jobname_str.thl }
  % end the group
  \group_end:
  \seq_map_inline:Nn \g_riccardo_theorems_counters_seq
   {
    \setcounter{##1@inner}{0}
   }
 }

\cs_new_protected:Nn \riccardo_theorems_save:
 {
  \seq_map_function:NN \g_riccardo_theorems_seq \__riccardo_theorems_save:n
 }
\cs_new_protected:Nn \__riccardo_theorems_save:n
 {
  \iow_now:Nn \g_riccardo_theorems_stream
   {
    \savedtheorem #1
   }
 }
\AtEndDocument
 {
  \iow_open:Nn \g_riccardo_theorems_stream { \c_sys_jobname_str.thl }
  \riccardo_theorems_save:
  \iow_close:N \g_riccardo_theorems_stream
 }

\NewDocumentCommand{\savedtheorem}{smmom}
 {
  % redefine \the<statement>@inner to yield the stored number
  % we use the property list to use the correct counter
  % (for instance, in case of "lemma", <statement> will be "theorem"
  \IfBooleanTF{#1}
   {
    \IfNoValueTF{#4}
     {\begin{#2@inner}#5\end{#2@inner}}
     {\begin{#2@inner}[#4]#5\end{#2@inner}}
   }
   {
    \cs_set:cpn { the \prop_item:Nn \g_riccardo_theorems_prop {#2} @inner } { #3 }
    \IfNoValueTF{#4}
     {
      % no attribution
      \begin{#2@inner} #5 \end{#2@inner}
     }
     {
      % attribution
      \begin{#2@inner}[#4] #5 \end{#2@inner}
     }
   }
 }

\ExplSyntaxOff

\newtheoremx*{theorem*}{Theorem}
\newtheoremx{theorem}{Theorem}[section]
\newtheoremx{lemma}[theorem]{Lemma}

\begin{document}

\section{Theorems}

\printtheorems

\section{First test}

\begin{theorem*}
This is not numbered.
\end{theorem*}

\begin{theorem}[Important]\label{thm:important}
This is a theorem about $\log_a x$.
\end{theorem}

\begin{lemma}\label{lem:whatever}
This is a lemma.
\end{lemma}

\begin{theorem}\label{thm:unimportant}
This is another theorem
\end{theorem}

\section{Second test}

\begin{theorem}\label{thm:soandso}
Again a theorem.
\end{theorem}

\end{document}

enter image description here

The following version requires xparse released 2019-03-05 or later.

\documentclass{article}
\usepackage{amsmath}
\usepackage{amsthm}
\usepackage{xparse}

\ExplSyntaxOn

\NewDocumentCommand{\newtheoremx}{smomo}
 {
  \IfBooleanTF{#1}
   {
    \printthm_nonumber:nn { #2 } { #4 }
   }
   {
    \printthm_number:nnnn { #2 } { #3 } { #4 } { #5 }
   }
 }

\cs_new_protected:Nn \printthm_nonumber:nn
 {
  \newtheorem*{#1@inner}{#2}
  \NewDocumentEnvironment{#1}{ o +b }
   {
    % start the inner environment (without or with optional argument)
    \IfNoValueTF { ##1 }
     { \begin{#1@inner} }
     { \begin{#1@inner}[##1] }
    % typeset the statement
    ##2
    % end the inner environment
    \end{#1@inner}
    % store the statement in a sequence variable, actually as
    % four arguments as shown
    \seq_gput_right:Nx \g_riccardo_theorems_seq
     {
      *
      { #1 } % name
      { } % number
      \tl_if_novalue:nF { ##1 } { [\exp_not:n { ##1 }] } % attribution
      { \exp_not:n { ##2 } } % body
     }
   }
   {}
 }

\cs_new_protected:Nn \printthm_number:nnnn
 {
  % * Let's duplicate the working of \newtheorem; \newtheoremx should
  %   be used for theorems that need to be listed
  % * \newtheoremx{theorem}{Theorem} will actually do
  %   \newtheorem{theorem@inner}{Theorem} (honoring the usual optional arguments)
  % * We also need a property list to store along with the theorem
  %   the one which it is subordinate to
  \tl_if_novalue:nTF { #2 }
   {
    \tl_if_novalue:nTF { #4 }
     {
      \newtheorem{#1@inner}{#3}
     }
     {
      \newtheorem{#1@inner}{#3}[#4]
     }
    % #1 is not subordinate, store the name itself
    \prop_gput:Nnn \g_riccardo_theorems_prop { #1 } { #1 }
    \seq_gput_right:Nn \g_riccardo_theorems_counters_seq { #1 }
   }
   {
    \newtheorem{#1@inner}[#2@inner]{#3}
    % #1 is subordinate to #2
    \prop_gput:Nnn \g_riccardo_theorems_prop { #1 } { #2 }
   }
  % define a "grabbing" environment #1 with the usual features 
  \NewDocumentEnvironment{#1}{ o +b }
   {
    % start the inner environment (without or with optional argument)
    \IfNoValueTF{##1}
     { \begin{#1@inner} }
     { \begin{#1@inner}[##1] }
    % save the statement number
    \tl_gset:Nx \g__riccardo_theorems_number_tl { \use:c {@currentlabel} }
    % typeset the statement
    ##2
    % end the inner environment
    \end{#1@inner}
   % store the statement in a sequence variable, actually as
   % four arguments as shown
   \seq_gput_right:Nx \g_riccardo_theorems_seq
     {
      { #1 } % name
      { \g__riccardo_theorems_number_tl } % number
      \IfValueT{##1}{ [\exp_not:n { ##1 }] } % attribution
      { \exp_not:n { ##2 } } } % body
    }
    {}
 }

% allocate the needed variables
\prop_new:N \g_riccardo_theorems_prop
\seq_new:N \g_riccardo_theorems_seq
\tl_new:N \g__riccardo_theorems_number_tl
\iow_new:N \g_riccardo_theorems_stream
\seq_new:N \g_riccardo_theorems_counters_seq

% print the stored theorems
\NewDocumentCommand{\printtheorems}{}
 {
  % we need a group where nullifying the action of \label
  \group_begin:
  \cs_set_eq:NN \label \use_none:n
  % map the sequence, passing each item to the function that prints a theorem
  \file_if_exist_input:n { \c_sys_jobname_str.thl }
  % end the group
  \group_end:
  \seq_map_inline:Nn \g_riccardo_theorems_counters_seq
   {
    \setcounter{##1@inner}{0}
   }
 }

\cs_new_protected:Nn \riccardo_theorems_save:
 {
  \seq_map_function:NN \g_riccardo_theorems_seq \__riccardo_theorems_save:n
 }
\cs_new_protected:Nn \__riccardo_theorems_save:n
 {
  \iow_now:Nn \g_riccardo_theorems_stream
   {
    \savedtheorem #1
   }
 }
\AtEndDocument
 {
  \iow_open:Nn \g_riccardo_theorems_stream { \c_sys_jobname_str.thl }
  \riccardo_theorems_save:
  \iow_close:N \g_riccardo_theorems_stream
 }

\NewDocumentCommand{\savedtheorem}{smmom}
 {
  % redefine \the<statement>@inner to yield the stored number
  % we use the property list to use the correct counter
  % (for instance, in case of "lemma", <statement> will be "theorem"
  \IfBooleanTF{#1}
   {
    \IfNoValueTF{#4}
     {\begin{#2@inner}#5\end{#2@inner}}
     {\begin{#2@inner}[#4]#5\end{#2@inner}}
   }
   {
    \cs_set:cpn { the \prop_item:Nn \g_riccardo_theorems_prop {#2} @inner } { #3 }
    \IfNoValueTF{#4}
     {
      % no attribution
      \begin{#2@inner} #5 \end{#2@inner}
     }
     {
      % attribution
      \begin{#2@inner}[#4] #5 \end{#2@inner}
     }
   }
 }

\ExplSyntaxOff

\newtheoremx*{theorem*}{Theorem}
\newtheoremx{theorem}{Theorem}[section]
\newtheoremx{lemma}[theorem]{Lemma}

\begin{document}

\section{Theorems}

\printtheorems

\section{First test}

\begin{theorem*}
This is not numbered.
\end{theorem*}

\begin{theorem}[Important]\label{thm:important}
This is a theorem about $\log_a x$.
\end{theorem}

\begin{lemma}\label{lem:whatever}
This is a lemma.
\end{lemma}

\begin{theorem}\label{thm:unimportant}
This is another theorem
\end{theorem}

\section{Second test}

\begin{theorem}\label{thm:soandso}
Again a theorem.
\end{theorem}

\end{document}

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.