About
Shop
LaTeX
Software
Books
Gallery
News
Contact
Blog
Settings
Account


9.3 The probsoln Package

The probsoln package [94] provides a means to define problems with their associated solution. These definitions may be placed in an external .tex file. You can then load all problems or a subset of problems (possibly randomly selected) into a dataset. You can have more than one dataset, each of which could, for example, represent a topic. In your document you can iterate through these datasets and display the problem, the solution or both. This means that you can gather the solutions together in another part of the document. Since probsoln is a package, you need to find an appropriate document class.

The probsoln package has the following options:

answers
Show the solutions.
noanswers
Hide the solutions (default).
draft
Display the problem label and dataset name when a problem is used.
final
Don't display the problem label and dataset name (default).
usedefaultargs
Make \thisproblem use the default arguments supplied with the problem definition.
nousedefaultargs
Make \thisproblem prompt for arguments (default).
The last two options will be described in more detail below. Remember that any options specified in \documentclass are also passed to packages, so if you use the draft class option, it will automatically enable probsoln's draft option, unless you have explicitly used probsoln's final option.

At the time of writing the current version of probsoln is version 3.04 (2012-08-23). Some of the features described here aren't available for earlier versions. Since defining the problems and their solutions requires either a command or an environment that gathers its contents, verbatim code requires special care. To allow for verbatim text, the probsoln package provides the fragile boolean key that can be used when defining a problem. If the majority of your problems require this option, you can set it using:

\setkeys{probsoln}{fragile}

(This command is provided by the keyval package [12], which is automatically loaded.)

In order to work with verbatim code, the probsoln package creates a temporary file that's used when the fragile option is set. The default name for this file is \jobname.vrb but if this conflicts with another package, you can change the extension by redefining

\ProbSolnFragileExt

You can also change the basename by redefining

\ProbSolnFragileFile

In addition to the answers and noanswers options, you can also show or suppress solutions using

\showanswers

(to show the solutions) and

\hideanswers

(to hide the solutions). These are declarations that can be scoped by placing them within a group.

You can check if the show solutions setting is on or off by testing the showanswers boolean flag. This can be done using:

\ifshowanswerstrue part⟩\else ⟨false part⟩\fi

or you can use the \ifthenelse command provided by the ifthen package:

\ifthenelse{\boolean{showanswers}}{true part}{false part}

or you can use the \ifbool command provided by the etoolbox package:

\ifbool{showanswers}{true part}{false part}

For example:

Assignment 1\ifbool{showanswers}{ (Solution Sheet)}{}

For longer text, you can use the environments onlyproblem

\begin{onlyproblem}[option]
text
\end{onlyproblem}

to only display ⟨text⟩ if the solutions are suppressed, and onlysolution

\begin{onlysolution}[option]
text
\end{onlysolution}

to only display ⟨text⟩ if the solutions are displayed. In both cases, the optional argument ⟨option⟩ may be the fragile boolean key, described above.

Example:

\begin{onlyproblem}
What is the main drawback of ray guns?
\end{onlyproblem}
\begin{onlysolution}
Overheating.
\end{onlysolution}

If the solutions are displayed, only the solution (“Overheating.”) will be typeset, otherwise only the question (“What is the main drawback of ray guns?”) will be typeset.

Note that spaces at the start of the environment are discarded but spaces at the end of the environment aren't. So the EOL character immediately before “What” is discarded (and similarly for the EOL character immediately before “Overheating”) but the EOL character after the question mark is interpreted as a space (and similarly for the EOL character after the full stop in the solution). If these spaces are unwanted, you can suppress them with the % comment character:

\begin{onlyproblem}
What is the main drawback of ray guns?%
\end{onlyproblem}%
\begin{onlysolution}
Overheating.%
\end{onlysolution}

If you want the question to always appear, regardless of the showanswers flag, then don't put it inside the onlyproblem environment:

What is the main drawback of ray guns?
\begin{onlysolution}
Overheating.%
\end{onlysolution}

You may prefer to put the solution inside the solution environment:

\begin{solution}text\end{solution}

which is equivalent to:

\par\noindent\textbf{\solutionname}: ⟨text

where \solutionname defaults to “Solution”.

Example:

What is the main drawback of ray guns?
\begin{onlysolution}
\begin{solution}
Overheating.
\end{solution}
\end{onlysolution}

The combination of the onlysolution and solution environments in this manner is analogous to the exam class's solution environment. Since it's possible that you may want to use the probsoln package with the exam class, if probsoln detects that the solution environment is already defined, it doesn't try defining its own solution environment. This means that if the above example was used with both the exam class and the probsoln package, there's a level of redundancy with the exam class performing a check for its own show/hide solution setting in its solution environment and the probsoln package performing a similar check for its showanswers flag in its onlysolution environment. Therefore, if you are using both exam and probsoln, I recommend you redefine the solution environment in a manner similar to probsoln's solution environment and just use probsoln's showanswers flag, as shown in Example 44.

The probsoln package provides an inline numbered list environment called textenum:

\begin{textenum}items\end{textenum}

This uses the same counter and label as the enumerate environment would use at the current level. For example, if the textenum environment was used outside the enumerate environment, the enumi counter will be used with \labelenumi, but if textenum was used inside a single enumerate environment, the enumii will be used with \labelenumii.

As with the standard list environments, each item is started with \item but no paragraph break is inserted unless you explicitly add one (either via a blank line or \par). You can also use

\correctitem

in place of \item to indicate a correct choice and

\incorrectitem

in place of \item to indicate an incorrect choice. If the solutions are suppressed, these two commands behave the same as \item. When the solutions are displayed, the default behaviour of \correctitem is to put a frame around the item marker, and the default behaviour of \incorrectitem is to just display the marker (as per \item). You can change this by redefining

\correctitemformat{marker}

which governs the format used by \correctitem, and

\incorrectitemformat{marker}

which governs the format used by \incorrectitem.

Example 44. Using Both the exam Class and the probsoln Package

This example uses the exam class with the probsoln package. The exam class's solution environment is redefined to make it more like the probsoln package's solution environment. The \ignorespaces and \ignorespacesafterend commands (introduced in Volume 1) suppress any spaces following the start and end of the environment that may be introduced by a spurious EOL character. The \noindent command (also introduced in Volume 1) suppresses the paragraph indentation.

With this redefinition of the solution environment, the exam class's \printanswers command no longer has an effect. Instead the probsoln package's \showanswers command should be used.

Since the textenum environment is an inline list, its items may be placed inside a tabular environment, as is done in the example document below.

\documentclass[addpoints]{exam}

\usepackage{probsoln}

\showanswers

\renewenvironment{solution}%
{\par\noindent\textbf{\solutionname}: \ignorespaces}%
{\ignorespacesafterend}

\renewcommand*{\theenumi}{\Alph{enumi}}

\begin{document}
\begin{center}\bfseries
Assignment~1\ifshowanswers\space (Solution Sheet)\fi
\end{center}

\begin{questions}
\question[1] What is the main drawback of ray guns?
\begin{onlysolution}
\begin{solution}
Overheating.
\end{solution}
\end{onlysolution}

\question[1] Which of the following is an ingredient of
mind-controlling cookies?
\begin{center}
 \begin{textenum}
  \begin{tabular}{ll}
  \incorrectitem arsenic &
  \incorrectitem cyanide \\
  \incorrectitem curare &
  \correctitem secret genetically modified sugar beet
  \end{tabular}
 \end{textenum}
\end{center}
\end{questions}

\end{document}

The resulting text is:

Assignment 1 (Solution Sheet)
  1. (1 point) What is the main drawback of ray guns?

    Solution: Overheating.

  2. (1 point) Which of the following is an ingredient of mind-controlling cookies?

    A. arsenic B. cyanide
    C. curare D. secret genetically modified sugar beet

(When the solutions are displayed the "D." marker is enclosed in a box.) End of Image.


You can download or view this example.

Thus far I haven't shown you anything that the exam class can't already do, so let's now look at how to define problems and their associated solutions for later use. A problem can be defined using the defproblem environment

\begin{defproblem}[n][default args]{label}[option]
text
\end{defproblem}

where ⟨text⟩ may include the onlyproblem or onlysolution environments (or a combination of both). The first optional argument ⟨n⟩ is the number of arguments this problem may take. Each argument may be referenced in ⟨text⟩ using the standard #1 etc method of referencing an argument. The second optional argument ⟨default args⟩ are the default arguments to use with this problem when it is automatically used by \thisproblem, described below. (The default argument is ignored when the problem is referenced with \useproblem, which must have the arguments explicitly added.)

If ⟨n⟩ is omitted, 0 is assumed and ⟨default args⟩ should also be omitted. The final optional argument ⟨option⟩ may be used to specify the fragile boolean key described earlier. If ⟨text⟩ contains any instances of the onlyproblem or onlysolution environments, they will inherit the fragile state from defproblem.

The only mandatory argument is ⟨label⟩, which is a label that uniquely identifies this problem so that it can later be referenced. As with all the other labelling systems described in this book, the label must not contain any special characters. The problem must be defined before use, typically either in the preamble or in an external .tex file.

Example:

Here's a simple example that doesn't require any arguments:

\begin{defproblem}{raygun}
 \begin{onlyproblem}
 What is the main drawback of ray guns?%
 \end{onlyproblem}%
 \begin{onlysolution}
 Overheating.%
 \end{onlysolution}
\end{defproblem}

Here's an example that requires one argument. This argument defaults to 2. Note that the braces { } are required for each argument.

\begin{defproblem}[1][{2}]{diffsin}
 \begin{onlyproblem}
 Differentiate $f(x) = \sin(#1x)$.
 \end{onlyproblem}%
 \begin{onlysolution}
 $f'(x) = #1\cos(#1x)$
 \end{onlysolution}
\end{defproblem}

In both of the above examples, I'm assuming that the solutions will be printed later in the document, separate from the question, so I haven't bothered to use the solution environment, since the “Solution:” tag is now redundant. If, on the other hand, I want a solution sheet that displays the solution with its associated question, then it's better to remove the onlyproblem environment and add the solution environment inside the onlysolution environment:

\begin{defproblem}{raygun}
 What is the main drawback of ray guns?
 \begin{onlysolution}
  \begin{solution}
  Overheating.%
  \end{solution}
 \end{onlysolution}
\end{defproblem}

Since it's quite cumbersome having to write so many \begin and \end commands, the probsoln package provides a convenient shortcut command:

\newproblem[n][default args]{label}{problem}{solution}

This is equivalent to:

\begin{defproblem}[n][default args]{label}%problem%
 \begin{onlysolution}%
 \begin{solution}%solution%
 \end{solution}%
 \end{onlysolution}%
\end{defproblem}

There is also a starred version:

\newproblem*[n][default args]{label}{problem}

which is a shortcut for:

\begin{defproblem}[n][default args]{label}%problem%
\end{defproblem}

Note that both versions of \newproblem don't support verbatim, so if your problem contains verbatim text you must use the defproblem environment (with the fragile key set).

Once you have defined your problems, you can the use them in your document. You can explicitly use a particular problem via the command:

\useproblem[dataset]{label}{arg 1}{argN}

where ⟨label⟩ is the label uniquely identifying the problem. If the problem was defined to have arguments, the arguments must then follow the label. The optional argument ⟨dataset⟩ indicates the dataset in which the problem is stored. If omitted the default dataset is assumed.

Example:

Given the definitions in the earlier example of the problems with the labels raygun and diffsin, these can now be used in the document:

\begin{enumerate}
\item \useproblem{raygun}

\item \useproblem{diffsin}{3}
\end{enumerate}

This is on the assumption that the problems were defined in the document preamble, which will automatically place them in the default dataset. Since the diffsin problem was defined to have one argument, that argument has to be provided.

If you define all your problems in external files, you can input each file using LaTeX's standard \input command, which will have the same effect as just defining all those problems within the document. Alternatively, you can load the problems into a particular dataset using one of the commands described below, where ⟨dataset⟩ is the name of the dataset and ⟨filename⟩ is the name of the file. If the dataset is omitted, the default dataset is assumed. Note that the dataset name mustn't contain any special characters.

\loadallproblems[dataset]{filename}

This loads all the problems defined in the given ⟨filename⟩ and adds them to the ⟨dataset⟩ indicated in the optional argument.

\loadselectedproblems[dataset]{labels}{filename}

This only defines the problems listed in the comma-separated listlabels⟩. The other problems in ⟨filename⟩ are ignored.

\loadexceptproblems[dataset]{exception list}{filename}

This is the reverse of \loadselectedproblems. It only defines the problems that aren't listed in the comma-separated listexception list⟩.

\loadrandomproblems[dataset]{n}{filename}

This loads ⟨n⟩ randomly selected problems defined in ⟨filename⟩ and adds them to the given dataset.

\loadrandomexcept[dataset]{n}{filename}{exception list}

Similar to the previous command but discounts the problems listed in ⟨exception list⟩ when making the random selection.

Once you have loaded your problems, using one of the above commands, you can iterate through a dataset using:

\foreachproblem[dataset]{body}

This does ⟨body⟩ at each iteration. Within ⟨body⟩ you can use

\thisproblem

to use the current problem and

\thisproblemlabel

to access the current problem label. Unlike \useproblem, you don't supply the problem arguments when you use \thisproblem. If the problem requires one or more arguments and no default arguments were provided when the problem was defined or the usedefaultargs package option wasn't used, then you will be prompted for the arguments, which requires LaTeX to be run in interactive mode. (Interactive mode is when LaTeX stops on encountering an error and prompts you for a response.)

Example:

To iterate through all problems in the default dataset:

\begin{enumerate}
\foreachproblem{\item\thisproblem}
\end{enumerate}

Assuming that you haven't switched on the solutions using \showanswers or the answers package option, this will just list all the problems. If you switch on the solutions, this will include the solutions but omit any text inside the onlyproblem environment.

There is also a similar command:

\foreachsolution[dataset]{body}

which behaves like \foreachproblem but only iterates over those problems that contain an onlysolution environment. (You must have first used the problems earlier in the document.) Note that you still need to switch on the show solution flag using \showanswers or the answers package option if you want the solutions displayed.

\foreachdataset{cs}{body}

This iterates over all the defined datasets and does ⟨body⟩ at each iteration. Within ⟨body⟩ you can use the control sequence ⟨cs⟩ to access the current dataset name.

For example, to display all problems in all datasets:

\begin{enumerate}
\foreachdataset{\thisdataset}%
{%
  \foreachproblem[\thisdataset]{\item\thisproblem}%
}
\end{enumerate}

Example:

Suppose I have some calculus problems defined in a file called calculus.tex and I have some linear algebra problems defined in a file called linearalgebra.tex, then in my document preamble I could, say, load five randomly selected calculus problems and four randomly selected linear algebra problems using:

\loadrandomproblems[calculusproblems]{calculus}
\loadrandomproblems[linearalgebraproblems]{linearalgebra}

(The .tex extension may be omitted.) This creates two datasets with the labels calculusproblems and linearalgebraproblems. In my document, I could simply iterate over all datasets using \foreachdataset as described above.

\begin{enumerate}
\foreachdataset{\thisdataset}%
{%
  \foreachproblem[\thisdataset]{\item\thisproblem}%
}
\end{enumerate}

Alternatively, I might want to divide the document into sections for each topic:

\section{Calculus}
\begin{enumerate}
\foreachproblem[calculusproblems]{\item\thisproblem}
\end{enumerate}

\section{Linear Algebra}
\begin{enumerate}
\foreachproblem[linearalgebraproblems]{\item\thisproblem}
\end{enumerate}

The seed used by the pseudo random number generator can be changed using:

\PSNrandseed{n}

where ⟨n⟩ is a non-zero number to use as the seed. Random numbers can be generated using:

\PSNrandom{register}{n}

which generates a random integer between 1 and ⟨n⟩ (inclusive) and stores it in the given count register (see §2.1.3 Arithmetic) or

\random{counter}{min}{max}

which generates a random integer between ⟨min⟩ and ⟨max⟩ (inclusive) and stores it in the LaTeX counter whose name is ⟨counter⟩. (See also §9.5 Random Numbers below.)

Example:

Recall the earlier diffsin problem. Suppose that instead of defining the problem to have an argument for the factor, the factor is randomly selected instead. First a new register needs to be defined:

\newcount\myrandarg

Now the problem can be defined:

\begin{defproblem}{randdiffsin}
 \PSNrandom{\myrandarg}{10}%
 Differentiate $f(x) = \sin(\the\myrandarg x)$.
 \begin{onlysolution}
 $f'(x) = \the\myrandarg\cos(\the\myrandarg x)$
 \end{onlysolution}
\end{defproblem}

If the random number seed is set to the current year:

\PSNrandseed\year

then this problem will appear differently if the same document is rebuilt on different years.

If you want the solutions to appear in a different location to the questions (for example, at the end of the document) you'll need to store the randomly generated number to prevent a different number from being generated in the solution section. For example:

\begin{defproblem}{randdiffsin}
 \ifundef\randdiffsinarg
 {%
  \PSNrandom{\myrandarg}{10}%
  \xdef\randdiffsinarg{\the\myrandarg}% globally store
 }
 {}%
 Differentiate $f(x) = \sin(\randdiffsinarg x)$.
 \begin{onlysolution}
 $f'(x) = \randdiffsinarg\cos(\randdiffsinarg x)$
 \end{onlysolution}
\end{defproblem}

(See also §9.5 Random Numbers.)

You can iterate over ⟨n⟩ randomly selected items in a comma-separated list using:

\doforrandN{n}{cs}{list}{body}

This performs ⟨body⟩ at each iteration where ⟨cs⟩ is a control sequence set to the currently selected item.

Example:

Suppose you have four files called file1.tex, file2.tex, file3.tex and file4.tex that all contain problem definitions, but you only want to load the problems defined in two of those files selected at random:

\doforrandN
 {2}% randomly select 2 items from the list
 {\thisfile}% command in which to store the current item
 {file1,file2,file3,file4}% the list
 {\loadallproblems{\thisfile}}

Example:

In this example, only one random selection is made and the selected item is saved for later use:

\doforrandN{1}{\thisitem}{cow,duck,chicken}
  {\global\let\thesubject\thisitem}%
\doforrandN{1}{\thisitem}{road,field,river}
  {\global\let\theobject\thisitem}%
Why did the \thesubject\␣cross the \theobject?
\begin{onlysolution}
 \begin{solution}
 So that the \thesubject\␣could get to the other side of the \theobject.
 \end{solution}
\end{onlysolution}

The previous warning also applies here if you intend to display the solutions in another part of the document. In this case, a similar approach can be used:

\begin{defproblem}{whycross}
 \ifdef{\thesubject}
 {}% already defined
 {%
  \doforrandN{1}{\thisitem}{cow,duck,chicken}
    {\global\let\thesubject\thisitem}%
  \doforrandN{1}{\thisitem}{road,field,river}
    {\global\let\theobject\thisitem}%
 }%
 \begin{onlyproblem}
  Why did the \thesubject\␣cross the \theobject?
 \end{onlyproblem}
 \begin{onlysolution}
 So that the \thesubject\␣could get to the other side of the \theobject.
 \end{onlysolution}
\end{defproblem}

This uses the \ifdef command provided by the etoolbox package described in §2.1.1 Macro Definitions.

Note that the pgfmath package also provides pseudo-random commands, which you may prefer to use. Some of these are described in §9.5 Random Numbers.

Example 45. Randomly Selecting Problems

In this example, I have a number of files containing problem definitions:

  1. mth101.tex

    This file contains 10 easy differentiation problems in the form:

    \begin{defproblem}{diff:sinx/x}
     \begin{onlyproblem}%
     $y = \frac{\sin x}{x}$.
     \end{onlyproblem}
     \begin{onlysolution}%
     \[\frac{dy}{dx} = \frac{\cos x}{x} - \frac{\sin x}{x^2}\]
     \end{onlysolution}%
    \end{defproblem}
    

    You can download the complete file.

  2. problems-1stprinciples.tex

    This file contains 5 differentiation from first principle problems in the form:

    \begin{defproblem}{dfp:cons}%
     \begin{onlyproblem}%
      Differentiate from first principles $f(x) = c$ where $c$ is a constant.%
     \end{onlyproblem}%
     \begin{onlysolution}%
     \begin{align*}
     \frac{df}{dx} & = \lim_{\Delta x\rightarrow 0}\frac{c-c}\Delta x\\
      & = \lim_{\Delta x\rightarrow 0}0\\
      & = 0
     \end{align*}%
     \end{onlysolution}%
    \end{defproblem}
    

    You can download the complete file.

I can now write a document that randomly selects 3 problems from the first file and 1 problem from the second file. The questions are listed first and the solutions later:

You can download or view this example document.


This book is also available as A4 PDF or 12.8cm x 9.6cm PDF or paperback (ISBN 978-1-909440-07-4).

© 2015 Dickimaw Books. "Dickimaw", "Dickimaw Books" and the Dickimaw parrot logo are trademarks. The Dickimaw parrot was painted by Magdalene Pritchett.

Terms of Use Privacy Policy Cookies Site Map FAQs