Hide content to show later
Here's a possibility:
\documentclass{book}
\usepackage{amsthm}
\usepackage{xparse}
\usepackage{environ}
\theoremstyle{definition} % body text is upright
\newtheorem{exercise}{Exercise}
\ExplSyntaxOn
\newtheorem*{innersolution}{Solution ~ of ~ \dvvv_exercise_ref:}
\NewEnviron{solution}
{
\seq_gput_right:Nx \g_dvvv_solution_group_seq { {\theexercise}{\exp_not:V \BODY} }
}
\NewDocumentEnvironment{solution*}{}
{
\cs_set:Npx \dvvv_exercise_ref: { \theexercise }
\innersolution
}
{
\endinnersolution
}
\NewDocumentCommand{\printsolutions}{}
{
\seq_map_inline:Nn \g_dvvv_solution_group_seq
{
\dvvv_print_solutions:nn ##1
}
\seq_gclear:N \g_dvvv_solution_group_seq
}
\seq_new:N \g_dvvv_solution_group_seq
\cs_new_protected:Npn \dvvv_print_solutions:nn #1 #2
{
\cs_set:Npn \dvvv_exercise_ref: { #1 }
\begin{innersolution} #2 \end{innersolution}
}
\ExplSyntaxOff
\begin{document}
\chapter{Title}
\section{Exercises}
\begin{exercise}
This is an exercise.
\end{exercise}
\begin{solution}
A solution.
\end{solution}
\begin{exercise}
This is an exercise.
\end{exercise}
\begin{solution}
A solution.
\end{solution}
\begin{exercise}
This is an exercise.
\end{exercise}
Some optional text in between.
\begin{solution*}
This is a solution.
\end{solution*}
The solution has been printed immediately, because
\texttt{solution*} has been used.
\begin{exercise}
This is another exercise.
\end{exercise}
\begin{solution}
A solution.
\end{solution}
\begin{exercise}
This is yet another exercise.
\end{exercise}
\begin{solution}
Too much.
\end{solution}
\section{Solutions}
\printsolutions
\end{document}
The solutions are stored in a FIFO list; using solution*
makes the solution print at the spot.
Here's the same, but with support for hyperlinks:
\documentclass{book}
\usepackage{amsthm}
\usepackage{xparse}
\usepackage{environ}
\usepackage[colorlinks]{hyperref}
\theoremstyle{definition} % body text is upright
\newtheorem{innerexercise}{Exercise}
\ExplSyntaxOn
\NewDocumentEnvironment{exercise}{}
{
\innerexercise\label{dvvv_ \theinnerexercise _label}
}
{
\endinnerexercise
}
\newtheorem*{innersolution}{\dvvv_exercise_ref:}
\NewEnviron{solution}
{
\seq_gput_right:Nx \g_dvvv_solution_group_seq
{
{\theinnerexercise}{\exp_not:V \BODY}
}
}
\NewDocumentEnvironment{solution*}{}
{
\cs_set:Npx \dvvv_exercise_ref:
{
\exp_not:N \hyperref
[
dvvv_ \theinnerexercise _label
]
{
Solution~of~\exp_not:N\ref*{dvvv_ \theinnerexercise _label}
}
}
\innersolution
}
{
\endinnersolution
}
\NewDocumentCommand{\printsolutions}{}
{
\seq_map_inline:Nn \g_dvvv_solution_group_seq
{
\dvvv_print_solutions:nn ##1
}
\seq_gclear:N \g_dvvv_solution_group_seq
}
\seq_new:N \g_dvvv_solution_group_seq
\cs_new_protected:Npn \dvvv_print_solutions:nn #1 #2
{
\cs_set:Npn \dvvv_exercise_ref:
{
\hyperref[dvvv_#1_label]{Solution~of~\ref*{dvvv_#1_label}}
}
\begin{innersolution} #2 \end{innersolution}
}
\ExplSyntaxOff
\begin{document}
\chapter{Title}
\section{Exercises}
\begin{exercise}
This is an exercise.
\end{exercise}
\begin{solution}
A solution.
\end{solution}
\begin{exercise}
This is an exercise.
\end{exercise}
\begin{solution}
A solution.
\end{solution}
\begin{exercise}
This is an exercise.
\end{exercise}
Some optional text in between.
\begin{solution*}
This is a solution.
\end{solution*}
The solution has been printed immediately, because
\texttt{solution*} has been used.
\begin{exercise}
This is another exercise.
\end{exercise}
\begin{solution}
A solution.
\end{solution}
\begin{exercise}
This is yet another exercise.
\end{exercise}
\begin{solution}
Too much.
\end{solution}
\section{Solutions}
\printsolutions
\end{document}
Let's look at the first solution step by step. The second just adds setting automatically a label for each exercise
environment, to be used for the hyperlink, so it's not much more complicated.
Step 1
Define an exercise
environment; this is standard
Step 2
Define an innersolution
environment as an unnumbered theorem; the label text depends on the value of \dvvv_exercise_ref:
that will be set at printing time
Step 3
Define a solution
environment, with the help of \NewEnviron
, that makes LaTeX store the environment's content in the macro \BODY
. The value of the macro, together with the current exercise number, is stored in a sequence.
Step 4
The solution*
environment just sets the value of \dvvv_exercise_ref:
to the current exercise number and calls innersolution
Step 5
Define the \printsolutions
command, which simply maps the sequence, that is, delivers to \dvvv_print_solutions:nn
each of its item. Finally it clears the sequence.
Step 6
The \dvvv_print_solutions:nn
function uses the fact that the solutions were stored in the format {<number>}{<contents>}
, so it uses the first argument for setting the value of \dvvv_exercise_ref:
and feeds the second argument to the innersolution
environment.
Links also in the reverse direction (from exercise to solution)
The trick is to add another label at solutions and a hyperlink at the exercise.
\documentclass{book}
\usepackage{amsthm}
\usepackage{xparse}
\usepackage{environ}
\usepackage[colorlinks]{hyperref}
\newtheoremstyle{exercise}% name
{\topsep}% Space above
{\topsep}% Space below
{\normalfont}% Body font
{}% Indent amount (empty = no indent, \parindent = para indent)
{\bfseries}% Thm head font
{.}% Punctuation after thm head
{ }% Space after thm head
{\hyperref[dvvv_#2_reverse_label]{\thmname{#1}\thmnumber{ #2}}\thmnote{ (#3)}}% Thm head spec
\theoremstyle{exercise} % body text is upright
\newtheorem{innerexercise}{Exercise}
\theoremstyle{definition}
\ExplSyntaxOn
\NewDocumentEnvironment{exercise}{}
{
\innerexercise\label{dvvv_ \theinnerexercise _label}
}
{
\endinnerexercise
}
\newtheorem*{innersolution}{\dvvv_exercise_ref:}
\NewEnviron{solution}
{
\seq_gput_right:Nx \g_dvvv_solution_group_seq
{
{\theinnerexercise}{\exp_not:V \BODY}
}
}
\NewDocumentEnvironment{solution*}{}
{
\phantomsection\label{dvvv_ \theinnerexercise _reverse_label}
\cs_set:Npx \dvvv_exercise_ref:
{
\exp_not:N \hyperref
[
dvvv_ \theinnerexercise _label
]
{
Solution~of~\exp_not:N\ref*{dvvv_ \theinnerexercise _label}
}
}
\innersolution
}
{
\endinnersolution
}
\NewDocumentCommand{\printsolutions}{}
{
\seq_map_inline:Nn \g_dvvv_solution_group_seq
{
\dvvv_print_solutions:nn ##1
}
\seq_gclear:N \g_dvvv_solution_group_seq
}
\seq_new:N \g_dvvv_solution_group_seq
\cs_new_protected:Npn \dvvv_print_solutions:nn #1 #2
{
\cs_set:Npn \dvvv_exercise_ref:
{
\phantomsection\label{dvvv_#1_reverse_label}
\hyperref[dvvv_#1_label]{Solution~of~\ref*{dvvv_#1_label}}
}
\begin{innersolution} #2 \end{innersolution}
}
\ExplSyntaxOff
\begin{document}
\chapter{Title}
\section{Exercises}
\begin{exercise}
This is an exercise.
\end{exercise}
\begin{solution}
A solution.
\end{solution}
\begin{exercise}
This is an exercise.
\end{exercise}
\begin{solution}
A solution.
\end{solution}
\begin{exercise}
This is an exercise.
\end{exercise}
Some optional text in between.
\begin{solution*}
This is a solution.
\end{solution*}
The solution has been printed immediately, because
\texttt{solution*} has been used.
\begin{exercise}
This is another exercise.
\end{exercise}
\begin{solution}
A solution.
\end{solution}
\begin{exercise}
This is yet another exercise.
\end{exercise}
\begin{solution}
Too much.
\end{solution}
\clearpage
\section{Solutions}
\printsolutions
\end{document}
UPDATE March 2019
With xparse
released 2019-03-05 or later, there's no need for environ
any longer. Replace the code for solution
by
\NewDocumentEnvironment{solution}{+b}
{
\seq_gput_right:Nx \g_dvvv_solution_group_seq
{
{\theinnerexercise}{ \exp_not:n { #1 } }
}
}
{}
Another solution could be done with tcolorbox
. This package provides commands \tcbstartrecording[file]
, \tcbstoprecording
and \tcbinputrecording[file]
which can serve as OP's \hidesolutions
and \stophidesolutions
.
Any tcolorbox
is divided into upper and lower parts. We can use the upper part as exercise and lower part as solution. Options savelowerto
creates ans auxiliary file where lower part is saved. This file can be saved for later use with record
option. And these recorded fragments can be included in our document with \tcbinputrecording
.
Next code shows an example based on codes taken from "7.3 Example: Exercises" and "13.9 Creation of LaTeX exercices" in tcolorbox
documentation.
I've also included tcblisting
boxes in my questions and solutions because it was written as answer to NewEnviron, \BODY and lstlisting which has been closed before I could post it. I think is also valid for this question.
Code contains some comments which I hope will serve to understand how it works
\documentclass{article}
\usepackage{lipsum}
\usepackage[most]{tcolorbox}
\usepackage{hyperref}
\NewTColorBox[auto counter,number within=section]{exercise}{+O{}}{%
enhanced, colframe=green!20!black, colback=yellow!10!white,
coltitle=green!40!black, fonttitle=\bfseries,
title={Exercise~\thetcbcounter:},
label={exercise@\thetcbcounter},
attach title to upper=\quad,
after upper={\par\hfill\textcolor{green!40!black}%
{\itshape Solution on page~\pageref{solution@\thetcbcounter}}},
lowerbox=ignored,
savelowerto=solutions/exercise-\thetcbcounter.tex,
record={\string\solution{\thetcbcounter}%
{solutions/exercise-\thetcbcounter.tex}},
#1}
\NewTotalTColorBox{\solution}{mm}{%
enhanced, colframe=red!20!black, colback=yellow!10!white,
coltitle=red!40!black, fonttitle=\bfseries,
title={Solution of Exercise~\ref{exercise@#1} on page~\pageref{exercise@#1}:},
phantomlabel={solution@#1},
attach title to upper=\par,
}{\input{#2}}
\tcbset{no solution/.style={no recording,after upper=}}
\newtcblisting{mylisting}{listing only}
\begin{document}
\section{Some problems}
% We start to record all `tcblower` parts in file `file1` for later provessing
\tcbstartrecording[file1]
%Every exercise box contains an upper part which is processed and
%printed and a lower (solution) which printing is delayed.
%
\begin{exercise}
Type some nice code
\begin{mylisting}
This is my
first listing
\end{mylisting}
\tcblower %<--------- Here upper part finishes and lower starts
The solution could be:
\begin{mylisting}
This is the solution
for the first listing problem
\end{mylisting}
\end{exercise}
\tcbstoprecording %<---- recording solutions in `file1` is finished.
% Recording mechanism has been stopped. Solutions are printed after
% upper part.
\begin{exercise}[lowerbox=visible] %<--- lowerbox is `invisible` in `exercise` boxes
Type some nice code
\tcblower
The solution could be:
\begin{mylisting}
My code 2
\end{mylisting}
\end{exercise}
% another file will continue recording solutions
\tcbstartrecording[file2]
\begin{exercise}
Type some nice code
\tcblower
The solution could be:
\begin{mylisting}
My code 3
\end{mylisting}
\end{exercise}
\tcbstoprecording
\section{Some solutions to previous problems}
%previous recorded files with solutions are processed and printed
\tcbinputrecords[file1]
\tcbinputrecords[file2]
\end{document}
Previous code is just an starting point. Some details, like references or style for not recorded solutions, must be solved. In any case, the result looks like: