6

I'm writing a solutions manual for a text. Some of the solutions are only supposed to be visible to the instructor, so I've written a macro

\newcommand{\instructor}[1]{}

which hides its argument. I use this macro in the student version of the manual, and a different definition in the instructor version.

The problem arises when used with knitr, to typeset R code. (knitr is a preprocessor that runs the code and inserts the output into the text.) If the R code contains a percent sign, knitr outputs something like

\begin{knitrout}
\definecolor{shadecolor}{rgb}{0.969, 0.969, 0.969}\color{fgcolor}  
\begin{kframe}
\begin{alltt}
\hlnum{1} \hlopt{%%} \hlnum{2}
\end{alltt}
\end{kframe}
\end{knitrout}

(This shows 1 %% 2.) When that appears as the argument to my \instructor macro, the percent signs are treated as comment markers, and the braces don't match, leading to an error.

So, my question: is there a way to do this that won't fail? I guess it needs to typeset the contents (so % is temporarily converted to a regular character) and then throw away the output.

Edited to add: Ulrike's solution works for the question as posed above, but things are actually more complicated, because I have enumerations where I would like to skip items on the student version. For example,

\documentclass{article}
\begin{document}

% This pair of macros is what I'm using now, but the
% student version (the first one) fails.

% \newcommand{\instructor[#1]{\addtocounter{enumi}{1}}
\newcommand{\instructor}{}

\begin{enumerate}

\item This is for everyone:
\catcode`\%=11
{ 12 %% 13 }
\catcode`\%=5

\instructor{
\item This is just for the instructor:
\catcode`\%=11
{ 12 %% 13 }
\catcode`\%=5
}

\end{enumerate}
\end{document}

I don't think the comment package offers a solution to replace the comment text with something completely different, like \addtocounter. Or does it?

3

Don't use a command with an argument -- this will always be difficult if catcode changes are involved. Better use the comment package:

\documentclass{article}

\usepackage{comment}
\includecomment{instructor}
%\excludecomment{instructor}
\begin{document}

bllb

\begin{instructor}
some text
\catcode`\%=11
12 %% 13
\catcode`\%=5
\begin{verbatim}
\section
\end{verbatim}

\end{instructor}

\end{document}

Attention: A quote from the documentation:

The opening and closing commands should appear on a line of their own. No starting spaces, nothing after it.

  • Thanks, that looks close -- but I left out one detail when simplifying to post here. I'd like the student version of the macro/environment to increment the item number in an enumeration, so it lists solutions to question 1, 3, 5, for example, while the instructor version lists 1, 2, 3, 4, 5. I don't think the comment package \specialcomment setting does this, but I might misunderstand it. – user42062 Jun 21 '16 at 14:53
  • @user2554330: Something like \specialcomment{instructor} {\begingroup\stepcounter{section}Missing instruction \thesection\ here \def\ProcessCutFile{}} {\endgroup} should work to cut out the text, but increment a counter (replace section by your counter). – Ulrike Fischer Jun 22 '16 at 7:40
  • Thanks! The \specialcomment definition in your comment works well. (I actually used \specialcomment{instructori}{\begingroup\stepcounter{enumi}\def\ProcessCutFile{}} {\endgroup}; the "i" at the end of the name is the enum level it works on.) – user42062 Jun 23 '16 at 11:33
1

This isn't a great solution, but it works. A better but more complicated solution is in the edit below.

Wrap each use of the \instructor{} macro that contains literal % characters in \catcode changes, e.g. in the original knitr code:

\catcode`\%=11
\instructor{
<<>>=
1 %% 2
@
}
\catcode`\%=5

A TeX wizard might know how to build that into the \instructor macro definition, but I don't.

This works with the following definitions of \instructor:

% For the student version:
\newcommand{\instructor}[1]{}

% For the instructor version:
\newcommand{\instructor}{}

I'd still appreciate more elegant solutions!

Edited to add:

Based on Ulrike's answer, I think I have a reasonably elegant solution now. It uses the comment package, and adds this macro definition:

\long\def\specialexclude
#1#2{\message{Excluding comment '#1'}%
\csarg\def{#1}{\endgroup \message{Excluding '#1' comment.}%
    \begingroup%
       \DefaultCutFileName #2\relax\def\ProcessCutFile{}%
       \def\ThisComment####1{}\ProcessComment{#1}}%
\csarg\def{After#1Comment}{\CloseAndInputCutFile \endgroup}
\CommentEndDef{#1}}

This adds a new environment which replaces its contents by an arbitrary command. You'd use it like this:

\documentclass{article}
\usepackage{comment}

\long\def\specialexclude
#1#2{\message{Excluding comment '#1'}%
\csarg\def{#1}{\endgroup \message{Excluding '#1' comment.}%
    \begingroup%
       \DefaultCutFileName #2\relax\def\ProcessCutFile{}%
       \def\ThisComment####1{}\ProcessComment{#1}}%
\csarg\def{After#1Comment}{\CloseAndInputCutFile \endgroup}
\CommentEndDef{#1}}

% For the student version:
\excludecomment{instructor}
\specialexclude{instructori}{\addtocounter{enumi}{1}}

% For the instructor version:
% \includecomment{instructor}
% \includecomment{instructori}

\begin{document}
\begin{enumerate}

\item This is for everyone:
\catcode`\%=11
{ 12 %% 13 }
\catcode`\%=5

\begin{instructori}
\item This is just for the instructor:
\catcode`\%=11
{ 12 %% 13 }
\catcode`\%=5
\end{instructori}

\item This is also for everyone.

\end{enumerate}
\end{document}
0

It might be easier just to break this into two flags: one for LaTeX and one for knitr. Changing line three to true and the logical in the first block to TRUE should evaluate to what you want.

\documentclass{article}
\usepackage{ifthen}
\newcommand{\isinstructor}{false} %change to false/true
\newcommand{\instructor}[1]{\ifthenelse{\equal{\isinstructor}{true}}{#1}{}}
<<include = FALSE>>=
instructor <- FALSE #change to FALSE/TRUE
@

\begin{document}
Everyone sees this:
<<eval = FALSE>>=
1 %% 2
@

\instructor{Only the instructor can see this}

<<include = instructor>>=
1 %% 2  # 1 + 2 would be okay
@

\end{document}

The requirement to include enumerate would also work as follows (note that the instructor counter is decremented to make it the same as the previous student question):

\documentclass{article}
\usepackage{ifthen}
\newcommand{\isinstructor}{false} %change to false/true
\newcommand{\instructor}[1]{\ifthenelse{\equal{\isinstructor}{true}}{\addtocounter{enumi}{-1} #1}{}}
<<include = FALSE>>=
instructor <- FALSE #change to FALSE/TRUE
@

\begin{document}

\begin{enumerate}
\item Everyone sees this:
<<eval = FALSE>>=
1 %% 2
@

\instructor{\item Only the instructor can see this}

<<include = instructor>>=
1 %% 2  # 1 + 2 would be okay
@
\end{enumerate}

\end{document}
  • That would work for many cases, but I think it would be less convenient than Ulrike's solution, because it would need special handling for the code chunks as well as the text. Some of the entries would have several code chunks separated by text. – user42062 Jun 23 '16 at 11:41

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy