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. –  Dec 01 '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 01 '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 –  Dec 01 '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 02 '17 at 01:11

2 Answers2

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.

egreg
  • 1,121,712
  • 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 02 '17 at 01: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 02 '17 at 01: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 02 '17 at 01:33
  • @ChristopherDonham I added a longer comment to my answer. – egreg Dec 02 '17 at 09:04
  • That makes more sense to me. If I could click on this for another "useful" point I would :-) – Christopher Donham Dec 02 '17 at 21:05
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

  • @StevenB.Segletes: Thanks ;-) I've updated with another (better?) way –  Dec 01 '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 02 '17 at 03: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 02 '17 at 03: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 02 '17 at 04:30
  • @ChristopherDonham: You have to specify the same itemsep etc. values to the answerenumerate \setlist –  Dec 02 '17 at 16:02
  • Ok. I understand. For my application, it is better to actually not redo the enumerate. The reason is that occasionally, I will miss an answer in a long review sheet. If that happens, then all subsequent answers would be numbered incorrectly. It is better for me to actually pass the enumerant value through to the answer sheet. Bottom line: I'm going with your "fake" solution as noted above. Thanks again. – Christopher Donham Dec 02 '17 at 21:08