6

My question appeared about this egreg's answer.

I have a list of exercise-answers but I don't want to show the answers immediately but after till I'll pull with a command and fetch all the answers till there.

By example:

\hidesolutions
\begin{exercise} this is an exercise.1 \end{exercise}
\begin{solution}  this is a solution for the above exercise.1  \end{solution}

\begin{exercise} this is an exercise.2 \end{exercise}
\begin{solution}  this is a solution for the above exercise.2  \end{solution}
\stophidesolutions

\begin{exercise} this is an exercise.3 \end{exercise}
\begin{solution}  this is a solution for the above exercise.3  \end{solution}

\hidesolutions
\begin{exercise} this is an exercise.4 \end{exercise}
\begin{solution}  this is a solution for the above exercise.4  \end{solution}

\begin{exercise} this is an exercise.5 \end{exercise}
\begin{solution}  this is a solution for the above exercise.5  \end{solution}
\stophidesolutions

\section{solutions}
\showsolutions 
\showsolutions

Results it should be:

  1. show exercises 1 and 2
  2. show exercise 3 and his answer
  3. show exercises 4 and 5
  4. show a new section solutions
  5. show answers 1 and 2
  6. show answers 4 and 5
9

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.

enter image description here

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}

enter image description here

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 } }
   }
 }
 {}
  • I should confess that I like the result by I need to learn more latex, your code looks so cryptic to me. Thanks a lot, btw. – jonaprieto Jan 2 '15 at 18:51
  • @d555 I added some explanations and also improved the hyperlink support, making all of “Solution of X” into a hyperlink. – egreg Jan 2 '15 at 19:04
  • How could one modify this example so that the exercise is linked to the solution (the same way the solution is linked to the corresponding exercise)? – mvienney Jul 6 '15 at 16:01
  • @MathieuVienney Good question. I'll try later. – egreg Jul 6 '15 at 16:11
  • 1
    @egreg. Your solution works just fine when you have a quite simple solution. But if I want to include for example a lstlisting, it doesn't work anymore. Do you think of an easy way of doing that ? Moreover, I noticed I can use a tcblisting inside exercise, but not inside solution. – mvienney Jul 24 '15 at 8:24
4

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:

enter image description here

  • I really like the footer for each exercise as well as the header on the solutions. Thanks for sharing. – jonaprieto Aug 2 '15 at 5:01

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.