2

I've got the following code, which uses the exam document class and the answers package. I'd like to pass the point value of the question into a separate file so that I can make use of the question's point value in a separate 'answer key' .tex file.

If I pass a literal number into the switch things work fine.

This works great

\documentclass[addpoints, 12pt]{exam}

% These provide a facility to collect up solutions/answers and grading guides into a separate file(s)
\printanswers 
\usepackage{answers}

\newcommand{\fnGradingGuide}{TempFile}

\Newassociation{sol}{Sol}{\fnGradingGuide} % ExamGradingGuideCore.tex is the intermediate file that stores stuff
\Newassociation{gradingGuide}{GradingGuide}{\fnGradingGuide}

\Opensolutionfile{\fnGradingGuide} 

% these are the macros that we'll use in the destination file to transform the things we stuff into the TempFile into something that the user will actually see
\renewenvironment{Sol}[2]{\textbf{Solution #1:\hfill#2 Points}\begin{flushleft}}{\end{flushleft}}
\renewenvironment{GradingGuide}[1]{\begin{flushleft}\textbf{Grading Guide for Question #1}\newline}{\end{flushleft}}

\newcommand{\QuestionPointValue}{30}

\begin{document}

    \begin{questions}


    \titledquestion{Syntax and Semantics}[\QuestionPointValue] 

    \textbf{Please write out what the program will print to the screen in this column} \\
    Please note that this code compiles, runs, and terminates. \

    \begin{sol}{30} <============ Note the literal '30' here!!!!!!!!!!!!!!!!!!!  This 30 is the points for the question
        This is the solution to question 1
    \end{sol}

    \end{questions}

    \Closesolutionfile{\fnGradingGuide}
    \centering {\Large Solutions} \\
    \input{\fnGradingGuide}

\end{document}

NOTE: This is a single-file example for simplicity, but it will still demonstrate the problem(s) I'm running into.

The contents of the extra file (TempFile.tex)will then contain:

TempFile:

\begin{Sol}{1}
{30} <====== THIS '30' IS THE THING I"M INTERESTED IN
^^I^^IThis is the solution to question 1
^^I
\end{Sol}

Now if I use a macro to define the point value (like this: \newcommand{\QuestionPointValue}{30}) and then pass that macro into the sol environment I'll get the following.

New version, this does NOT work:

\documentclass[addpoints, 12pt]{exam}

% These provide a facility to collect up solutions/answers and grading guides into a separate file(s)
\printanswers 
\usepackage{answers}

\newcommand{\fnGradingGuide}{TempFile}

\Newassociation{sol}{Sol}{\fnGradingGuide} % ExamGradingGuideCore.tex is the intermediate file that stores stuff
\Newassociation{gradingGuide}{GradingGuide}{\fnGradingGuide}

\Opensolutionfile{\fnGradingGuide} 
\renewenvironment{Sol}[2]{\textbf{Solution #1:\hfill#2 Points}\begin{flushleft}}{\end{flushleft}}
\renewenvironment{GradingGuide}[1]{\begin{flushleft}\textbf{Grading Guide for Question #1}\newline}{\end{flushleft}}

\newcommand{\QuestionPointValue}{30} % <====== This time we're using this

\begin{document}

    \begin{questions}


    \titledquestion{Syntax and Semantics}[\QuestionPointValue] 

    \textbf{Please write out what the program will print to the screen in this column} \\
    Please note that this code compiles, runs, and terminates. \

    \begin{sol}{\QuestionPointValue}  % <======= literal 30 has been replaced by the macro/newcommand
        This is the solution to question 1
    \end{sol}

    \renewcommand{\QuestionPointValue}{10}
    \titledquestion{Q2}[\QuestionPointValue] 

    \textbf{Q2 body}

    \begin{sol}{\QuestionPointValue}
        This is the solution to question 2
    \end{sol}

    \end{questions}

    \Closesolutionfile{\fnGradingGuide}
    \centering {\Large Solutions} \\
    \input{\fnGradingGuide}

\end{document}

This doesn't work because TempFile contains the new command instead of the fully-expanded value of 30:

\begin{Sol}{1}
{\QuestionPointValue}  <================= I was hoping this would still be 30, but instead it's the new command instead
^^I^^IThis is the solution to question 1
^^I
\end{Sol}

When I then include or input that fragment it won't work unless \QuestionPointValue is already defined, then it will use that single value everywhere that \QuestionPointValue is found in TempFile

Is there a way to define \QuestionPointValue so that I can use it as a 'parameter' to TempFile, when I finally include it in another .TeX file that will generate my answer key?

I tried using \the in a couple of places but my nearly-blind guessing didn't work, sadly

  • 2
    It should be noted that the points are already stored as \pointsofq@i etc. Admittedly you will need two runs and \csname pointsofq@\roman{question}\endscname to access them. \@ifundefined is also a good idea. – John Kormylo Sep 6 '19 at 15:02
3

The sol environment tries hard to write the exact contents of the environment to the auxiliary file.

You can automatize your trick of using \Writetofile, though, by patching the definition of \sol.

\documentclass[addpoints, 12pt]{exam}
\usepackage{answers}
\usepackage{etoolbox}

% These provide a facility to collect up solutions/answers and grading 
% guides into a separate file(s)
\printanswers 

\newcommand{\fnGradingGuide}{TempFile}

% ExamGradingGuideCore.tex is the intermediate file that stores stuff
\Newassociation{sol}{Sol}{\fnGradingGuide} 
\Newassociation{gradingGuide}{GradingGuide}{\fnGradingGuide}

\preto{\sol}{%
  \Writetofile{\fnGradingGuide}{\def\string\QuestionPointValue{\QuestionPointValue}}%
}

\Opensolutionfile{\fnGradingGuide} 
\renewenvironment{Sol}[2]
  {\textbf{Solution #1:\hfill#2 Points}\begin{flushleft}}
  {\end{flushleft}}
\renewenvironment{GradingGuide}[1]
  {\begin{flushleft}\textbf{Grading Guide for Question #1}\newline}
  {\end{flushleft}}

\newcommand{\QuestionPointValue}{30} % <====== This time we're using this

\begin{document}

    \begin{questions}

    \titledquestion{Syntax and Semantics}[\QuestionPointValue] 

    \textbf{Please write out what the program will print to the screen in this column} \\
    Please note that this code compiles, runs, and terminates.

    \begin{sol}{\QuestionPointValue}
        This is the solution to question 1
    \end{sol}

    \renewcommand{\QuestionPointValue}{10}
    \titledquestion{Q2}[\QuestionPointValue] 

    \textbf{Q2 body}

    \begin{sol}{\QuestionPointValue}
        This is the solution to question 2
    \end{sol}

    \end{questions}

    \Closesolutionfile{\fnGradingGuide}
    \begin{center}\Large Solutions\end{center}
    \input{\fnGradingGuide}

\end{document}

enter image description here

| improve this answer | |
3

I managed to workaround the problem, by using a combination of commands defined in the answer package. It's an ugly solution, because you need to copy and paste the same four lines: I can't manage to embed them inside a \newcommand or in a \newenvironment.

Here is the solution, the details on how it works are below. Simply, you have to copy and paste the following four lines before each sol environment:

    \Writetofile{\fnGradingGuide}{\def\pippo{\QuestionPointValue}}
    \begin{Filesave}{\fnGradingGuide}
\renewcommand{\QuestionPointValue}{\pippo}
    \end{Filesave}

Here is the whole document:

\documentclass[addpoints, 12pt]{exam}

% These provide a facility to collect up solutions/answers and grading guides into a separate file(s)
\printanswers 
\usepackage{answers}

\newcommand{\fnGradingGuide}{TempFile}

\Newassociation{sol}{Sol}{\fnGradingGuide}
\Newassociation{gradingGuide}{GradingGuide}{\fnGradingGuide}

\Opensolutionfile{\fnGradingGuide} 
\renewenvironment{Sol}[2]{\textbf{Solution #1:\hfill#2 Points}\begin{flushleft}}{\end{flushleft}}
\renewenvironment{GradingGuide}[1]{\begin{flushleft}\textbf{Grading Guide for Question #1}\newline}{\end{flushleft}}

\newcommand{\QuestionPointValue}{30}

%%%%%%% THIS IS ONE IMPORTANT LINE
\let\pippo\protect

\begin{document}

    \begin{questions}

%%% Q1
    \titledquestion{Syntax and Semantics}[\QuestionPointValue] 

    \textbf{Please write out what the program will print to the screen in this column} \\
    Please note that this code compiles, runs, and terminates. \

%% THESE ARE THE LINES YOU NEED TO COPY AND PASTE JUST BEFORE EACH sol ENVIRONMENT
    \Writetofile{\fnGradingGuide}{\def\pippo{\QuestionPointValue}}
    \begin{Filesave}{\fnGradingGuide}
\renewcommand{\QuestionPointValue}{\pippo}
    \end{Filesave}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    \begin{sol}{\QuestionPointValue}
        This is the solution to question 1
    \end{sol}

%%% Q2
    \renewcommand{\QuestionPointValue}{10}
    \titledquestion{Q2}[\QuestionPointValue] 

    \textbf{Q2 body}


    \Writetofile{\fnGradingGuide}{\def\pippo{\QuestionPointValue}}
    \begin{Filesave}{\fnGradingGuide}
\renewcommand{\QuestionPointValue}{\pippo}
    \end{Filesave}
    \begin{sol}{\QuestionPointValue}
        This is the solution to question 2
    \end{sol}

%%% Q3
\renewcommand{\QuestionPointValue}{50}
    \titledquestion{Q3}[\QuestionPointValue] 

    \textbf{Q3 body}
    \Writetofile{\fnGradingGuide}{\def\pippo{\QuestionPointValue}}
    \begin{Filesave}{\fnGradingGuide}
\renewcommand{\QuestionPointValue}{\pippo}
    \end{Filesave}
    \begin{sol}{\QuestionPointValue}
        This is the solution to question 3
    \end{sol}

    \end{questions}

    \Closesolutionfile{\fnGradingGuide}
    \centering {\Large Solutions} \\
    \input{\fnGradingGuide}

\end{document}

As you can see this is the result:

How it works?

  1. It writes on the file TempFile.tex the definition of the \newcommand explicitly before each Sol environment.
  2. In order to do so, it uses the command Writetofile (that writes some strings on the designated file, making the substitution of \newcommand commands) and the environment Filesave (that allows to write on the file LaTeX code as is, without making substitution of commands and so on).
  3. The syntax is a bit tough to handle to do this thing, in fact, in order to define properly \QuestionPointValue you have to use Filesave, but the latter doesn't substitute the contents, so you need to define another command, called \pippo and define it through \Writetofile.
  4. Though, if you use \pippo inside \Writetofile it gives error, because it tries to substitute it with it content. The only way is to put before \protect (as written in the documentation of answer) to say to \Writetofile that you are writing a keyword. But at the end, \protect remains in the definition and you are unable to correctly run \def.
  5. So you need to define \protect itself, and, in order to not destroy its definition, I put the line \let\pippo\protect in the preamble, so as \pippo now is equal to \protect and you can redefine simply \pippo.

I hope this solves your problem. Then, if you need to include TempFile.tex in another document, remember to define the command \QuestionPointValue in the preamble, otherwise the first \renewcommand won't recognise it.


Edit

Thanks to a suggestion of Willie Wong, my answer can be simplified. As he said

command \Writetofile expands the second argument twice

so you can simply write the single line:

\Writetofile{\fnGradingGuide}{\def\noexpand\noexpand\noexpand\QuestionPointValue{\QuestionPointValue}}

before each sol environment.

Of course you can create a command as:

\newcommand{\PrintPoint}[1]{\Writetofile{\fnGradingGuide}{\def\noexpand\noexpand\noexpand #1{#1}}}

that you can use as

\PrintPoint{\QuestionPointValue}

before each sol environment.

| improve this answer | |
  • 2
    You can simplify your solution somewhat, without playing with the \pippo workaround. A little bit of testing shows that \Writetofile expands the second argument twice. So you can just write \Writetofile{<filename>}{\def\noexpand\noexpand\noexpand\QuestionPointValue{\QuestionPointValue}} then this will write to the file \def\QuestionPointValue{<the value of \QuestionPointValue>}. – Willie Wong Sep 6 '19 at 14:38
  • 1
    That's a good point, I haven't thought about it. Thank you @WillieWong for your suggestion. I've updated my answer. – Qubitol Sep 6 '19 at 15:14
  • 2
    Even better would be to define a command that combines your \PrintPoint together with the call to \renewcommand\QuestionPointValue in the body. (Justification: presumably each time the QuestionPointValue is to be redefined the corresponding redefinition would be needed in the solutions file.) :-) – Willie Wong Sep 6 '19 at 15:26
  • 1
    That's actually a good idea! Moreover, an alternative would be to embed the \PrintPoint command inside the environment sol, but I have not managed to do that. – Qubitol Sep 6 '19 at 15:38

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.