11

I am a teacher and often need to make worksheets and reviews that have answer keys. With the help of TeX StackExchange, I have a fairly robust system at this point with the notable exception that it does not work with enumitem. The error message that I am getting is beyond my LaTeX wisdom. Is there anyone who can help me out?

MWE:

\documentclass[10pt]{article}

\usepackage{pgfplots}
\usepackage{enumitem}

\newwrite\answerkeyfile

\AtBeginDocument{%
   \immediate\openout\answerkeyfile=\jobname.keys
}

\begingroup
\catcode`\#=12
\gdef\hashchar{#}%
\endgroup

\makeatletter
\newcommand{\answerkey}[1]{%
  \begingroup
  \let\#\hashchar
  \immediate\write\answerkeyfile{\@currentlabel.\ }
  \immediate\write\answerkeyfile{\unexpanded{#1}}%
  \immediate\write\answerkeyfile{ }
  \endgroup
}
\makeatother

\AtEndDocument{%
  \immediate\closeout\answerkeyfile
}

\begin{document}

Answer these questions:

%% With \begin{enumerate}, this code works.  Switch to 
%% \begin{enumerate}[label=\emph{\alph*)}] and it fails.
%\begin{enumerate}
\begin{enumerate}[label=\emph{\alph*)}]
   \item Question 1 \answerkey{Answer 1}
   \item Question 2 \answerkey{$\frac{1}{2}$}
   \item Question 3 \answerkey{\begin{tikzpicture}
                          \draw (0,0) rectangle (1,1);
                          \end{tikzpicture}}
\end{enumerate}

Answer Key:

% Copy the mwe.keys to mwe.ans, then use this to show the answers.
%\input{mwe.ans}

\end{document}

As noted in the comments, switch the enumerate line to the one that uses enumitem features and this will fail. What can I do to not compromise the system I have and yet still use enumitem?

Here is the error that I am getting:

! Use of \@item doesn't match its definition. \text@command #1->\def \reserved@a {#1}\ifx \reserved@a \@empty \let \check@... l.37 \item Question 1 \answerkey{Answer 1}

UPDATE:

The comment below that it is not actually the enumitem code but rather the \emph (or similarly \textbf, \textit, etc) in the label that is the issue was interesting. The solution below that creates a "fake" label is the least invasive/complicated, while adding the functionality that label formatting makes it through to the answer key document. The code I started with did not put the answers into a true enumerate environment, so the solution proposed below which does this is overkill for my situation. In the end, based on the help/guidance below, I rewrote the \answerkey command as follows:

\newcommand{\answerkey}[2][.]{%
  \begingroup
  \let\#\hashchar
  \immediate\write\answerkeyfile{\expandafter\unexpanded
         \expandafter{\@currentlabel}#1\ }
  \immediate\write\answerkeyfile{\unexpanded{#2}}%
  \immediate\write\answerkeyfile{ }
  \endgroup
}

In actual use, it looks like this:

If the default item labels are used:

\begin{enumerate}
    \item Question 1 \answerkey{Answer 1}

If enumitem label formatting is used:

\begin{enumerate}[label={\arabic*.}]
    \item Question 1 \answerkey[]{Answer 1}
  • @StevenB.Segletes: I suggest to use \protected@write{\answerkeyfile}{}{\@currentlabel.\ } rather. \noexpand will result in \@currentlabel being written to the file which is probably not wanted. – user31729 Dec 1 '17 at 20:32
  • @ChristianHupfer You are correct in noting that my approach does not output the label properly. I will delete the earlier comment. – Steven B. Segletes Dec 1 '17 at 20:34
  • @StevenB.Segletes: Unfortunately my suggestion does only provide compilation but not the desired content in the output file. \@currentlabel seems to be deeply unexpandable here – user31729 Dec 1 '17 at 20:46
  • Is there a way to explain to a non-power-user what it is about enumitem that requires the special handling? Why is it that I can put a whole tikz picture into my answer file without troubles, yet I can't have the enumerant written there without special processing? – Christopher Donham Dec 2 '17 at 1:11
7

I suggest to expand \@currentlabel and write that content to the file with \unexpanded, however, this will place 'fake' item labels and not really \item layout in the file.

A better strategy would be to 'replicate' the enumerate environment in the output file.

\documentclass[10pt]{article}

\usepackage{pgfplots}
\usepackage{enumitem}

\newwrite\answerkeyfile

\AtBeginDocument{%
   \immediate\openout\answerkeyfile=\jobname.keys
}

\begingroup
\catcode`\#=12
\gdef\hashchar{#}%
\endgroup

\makeatletter
\newcommand{\answerkey}[1]{%
  \begingroup
  \let\#\hashchar
  \immediate\write\answerkeyfile{\expandafter\unexpanded\expandafter{\@currentlabel}.\ }
  \immediate\write\answerkeyfile{\unexpanded{#1}}%
  \immediate\write\answerkeyfile{ }
  \endgroup
}
\makeatother

\AtEndDocument{%
  \immediate\closeout\answerkeyfile
}

\begin{document}

Answer these questions:

%% With \begin{enumerate}, this code works.  Switch to 
%% \begin{enumerate}[label=\emph{\alph*)}] and it fails.
%\begin{enumerate}
\begin{enumerate}[label=\emph{\alph*)}]
   \item Question 1 \answerkey{Answer 1}
   \item Question 2 \answerkey{$\frac{1}{2}$}
   \item Question 3 \answerkey{\begin{tikzpicture}
                          \draw (0,0) rectangle (1,1);
                          \end{tikzpicture}}
\end{enumerate}

Answer Key:

% Copy the mwe.keys to mwe.ans, then use this to show the answers.
\immediate\closeout\answerkeyfile
\input{\jobname.keys}

\end{document}

Another approach by writing \begin{answerenumerate} to the file, which is a copy of enumerate, basically:

\documentclass[10pt]{article}

\usepackage{pgfplots}
\usepackage{enumitem}

\newwrite\answerkeyfile

\AtBeginDocument{%
   \immediate\openout\answerkeyfile=\jobname.keys
}

\begingroup
\catcode`\#=12
\gdef\hashchar{#}%
\endgroup

\makeatletter
\newcommand{\answerkey}[1]{%
  \begingroup
  \let\#\hashchar
  \immediate\write\answerkeyfile{\string\item}
  \immediate\write\answerkeyfile{\unexpanded{#1}}%
  \immediate\write\answerkeyfile{ }
  \endgroup
}
\makeatother

\AtEndDocument{%
  \immediate\closeout\answerkeyfile
}

\begin{document}

Answer these questions:

\newlist{answerenumerate}{enumerate}{1}
\setlist[answerenumerate]{label=\emph{\alph*)}}


\setlist[enumerate,1]{label=\emph{\alph*)},
  before={\immediate\write\answerkeyfile{\string\begin{answerenumerate}}},
    after={\immediate\write\answerkeyfile{\string\end{answerenumerate}}}
}

\begin{enumerate}
   \item Question 1 \answerkey{Answer 1}
   \item Question 2 \answerkey{$\frac{1}{2}$}
   \item Question 3 \answerkey{\begin{tikzpicture}
                          \draw (0,0) rectangle (1,1);
                          \end{tikzpicture}}
\end{enumerate}

Answer Key:

% Copy the mwe.keys to mwe.ans, then use this to show the answers.
\immediate\closeout\answerkeyfile
\input{\jobname.keys}

\end{document}

enter image description here

  • Very nice. (+1) – Steven B. Segletes Dec 1 '17 at 21:01
  • @StevenB.Segletes: Thanks ;-) I've updated with another (better?) way – user31729 Dec 1 '17 at 21:12
  • I have been playing around with your "fake" solution. At first glance, the "Answer" formatting appears to be very similar to the "Question" formatting. Can you suggest what might reveal the problems with this solution (or when I might encounter difficulties with it)? – Christopher Donham Dec 2 '17 at 3:38
  • The only way I have been able to reveal it so far is by using the default item labels in which case the "Answer" version does not have a period after the label. – Christopher Donham Dec 2 '17 at 3:59
  • Actually, I believe I have an answer to my own comment here. The formatting in the Answer portion does not maintain things like the "itemsep" or "topsep" settings from enumitem. With respect to the labels, it appears to be the same except for the default label which puts a period after the label. – Christopher Donham Dec 2 '17 at 4:30
8

You should use the ref feature of enumitem, also exploiting the fact that the package braces the \@currentlabel.

Define \answeritem as you like.

\documentclass[10pt]{article}

\usepackage{pgfplots}
\usepackage{enumitem}

\newwrite\answerkeyfile

\AtBeginDocument{%
   \immediate\openout\answerkeyfile=\jobname.keys
}

\edef\hashchar{\string#}

\makeatletter
\newcommand{\answerkey}[1]{%
  \begingroup
  \let\#\hashchar
  \immediate\write\answerkeyfile{\answeritem\@currentlabel}
  \immediate\write\answerkeyfile{\unexpanded{#1}^^J}%
  \endgroup
}
\protected\def\answeritem#1{\item[\emph{#1})]}
\makeatother

\begin{document}

Answer these questions:

%% With \begin{enumerate}, this code works.  Switch to 
%% \begin{enumerate}[label=\emph{\alph*)}] and it fails.
%\begin{enumerate}
\begin{enumerate}[label=\emph{\alph*}),ref=\alph*]
   \item Question 1 \answerkey{Answer 1}
   \item Question 2 \answerkey{$\frac{1}{2}$}
   \item Question 3 \answerkey{\begin{tikzpicture}
                          \draw (0,0) rectangle (1,1);
                          \end{tikzpicture}}
\end{enumerate}

Answer Key:

\immediate\closeout\answerkeyfile
\begin{enumerate}
\input{\jobname.keys}
\end{enumerate}

\end{document}

Here's the output in \jobname.keys:

\answeritem {a}
Answer 1

\answeritem {b}
$\frac {1}{2}$

\answeritem {c}
\begin {tikzpicture} \draw (0,0) rectangle (1,1); \end {tikzpicture}

enter image description here

Some more words about the problem.

The key ref defines what's stored in \@currentlabel (typically written in the .aux file, but you're using it yourself). Writing in the .aux file is safe, because \protected@write is used by LaTeX; however you're using \write and \emph is not a command TeX likes to see there. You could use \protected@iwrite (you find how to define it on the site), but the trick I suggest above is even better because you can then decide what \answeritem gets expanded to. It's important its definition is \protected, so it will remain untouched during the \write operation.

  • Can you help me to understand your solution? I don't see what the "ref" statement in the enumitem is achieving and can't find a good reference (bad pun) in the enumitem documentation (in other words, I read what it said in the enumitem documentation, and still don't understand the purpose of "ref" in this context). – Christopher Donham Dec 2 '17 at 1:20
  • @ChristopherDonham The error message you get is misleading; it was due to \@currentlabel containing \emph, which is not a command that TeX likes to see in the argument to \write. – egreg Dec 2 '17 at 1:31
  • So how does your "ref" help? The documentation seems to use "ref" in conjunction with "\ref", but I'm not seeing any \ref in your code (unless I am missing it). – Christopher Donham Dec 2 '17 at 1:33
  • @ChristopherDonham I added a longer comment to my answer. – egreg Dec 2 '17 at 9:04
  • That makes more sense to me. If I could click on this for another "useful" point I would :-) – Christopher Donham Dec 2 '17 at 21:05

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.