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