1

Building upon what I was doing in this thread, why didn't my \xdef work?

The Exercise Page comes out just fine. Ideally, the Answer Page would look identical, except, of course with the answers there.

But the Answers Page is gobbledygook. And sometimes it compiles and sometimes it doesn't.

The code works perfectly if I comment out the \AnswerList near the bottom. So the problem is related to that.

What gives?

Here's the code.

\documentclass{article}

\usepackage[margin=2cm]{geometry}
\usepackage{ifthen}
\usepackage{pgf}
\usepackage{pgffor}
\usepackage{tikz}

\setlength{\parindent}{0pt} 

\pgfmathsetseed{\number\pdfrandomseed}

\newcommand{\InitVariables}
{
 \pgfmathsetmacro{\FactorA}{int(random(1,9))}
 \pgfmathsetmacro{\FactorB}{int(random(1,9))}
 \pgfmathsetmacro{\Product}{int(\FactorA*\FactorB)}
 \pgfmathsetmacro{\Structure}{int(random(0,2))}
}

\newcommand{\AnswerSpace}
{%
 \tikz[baseline=6pt]{\draw (0,0)rectangle(0.9,0.9);}
}

\newcommand{\ASF}[1] % Answer Space Filled
{%
 \tikz[baseline=6pt]{\draw (0,0)rectangle(0.9,0.9); \node[center] at (0.45,0.35) {\color{blue}#1};}
}

\newcommand{\OneEquation}
{%
 \large
 \InitVariables
 \ifcase\Structure\relax
  \def\Exercise{\(\FactorA \times \FactorB = \AnswerSpace\)}
  \def\Answer{\(\FactorA \times \FactorB =\ASF{\Product}\)}
  \or
  \def\Exercise{\(\FactorA \times \AnswerSpace = \Product\)}
  \def\Answer{{\(\FactorA \times \ASF{\FactorB} = \Product\)}}
  \or
  \def\Exercise{\(\AnswerSpace \times \FactorB = \Product\)}
  \def\Answer{\(\ASF{\FactorA} \times \FactorB = \Product\)}
 \fi
}

\newcommand{\AnswerList}{}

\newcommand{\ExerciseList}[1]
{%
 \foreach \x in {1,2,3,...,{#1}} 
 {%
  \OneEquation \Exercise \par \vspace{0.5cm}
   \xdef\AnswerList{\AnswerList \Answer \par \vspace{0.5cm}}
 }
}




\begin{document}

\section{Exercises}
\ExerciseList{10}

\pagebreak
\section{Answers}
\AnswerList

\end{document}
  • Too many reasons. An easy one is that \vspace cannot be used in \xdef, but it's the simplest. – egreg Oct 7 '16 at 22:59
  • Ok. How else am I supposed to create vertical space between the lines then? And what were the other problems? I thought my code closely resembled the code from the prior thread (see link in OP). – WeCanLearnAnything Oct 7 '16 at 23:02
  • \noexpand\vspace solves the problem. Or \protected@xdef rather than \xdef. Also, there's a problem with \Answer and the previous contents of \AnswerList. You should define with \protected\def macros that you don't want to expand inside \(x|e)def (you can look at etoolbox for more flexibility). Also, is there a real reason to use \pgfmathsetseed{\number\pdfrandomseed} rather than \pgfmathsetseed{3324} (or whatever fixed number that you get randomly in any generator?). – Manuel Oct 7 '16 at 23:03
  • pgfmathsetseed{\number\pdfrandomseed} is code that I've copy/pasted but do not understand. – WeCanLearnAnything Oct 7 '16 at 23:12
  • I don't quite understand your other comments! I think the jargon might be a bit beyond me. – WeCanLearnAnything Oct 7 '16 at 23:13
4

You can not place arbitrary tex commands in an xdef/edef if they do not work by expansion they will fail you don't want to fully expand it, just expand the numeric parts, one step.

\documentclass{article}

\usepackage[margin=2cm]{geometry}
\usepackage{ifthen}
\usepackage{pgf}
\usepackage{pgffor}
\usepackage{tikz}

\setlength{\parindent}{0pt} 

\pgfmathsetseed{\number\pdfrandomseed}

\newcommand{\InitVariables}
{
 \pgfmathsetmacro{\FactorA}{int(random(1,9))}
 \pgfmathsetmacro{\FactorB}{int(random(1,9))}
 \pgfmathsetmacro{\Product}{int(\FactorA*\FactorB)}
 \pgfmathsetmacro{\Structure}{int(random(0,2))}
}

\newcommand{\AnswerSpace}
{%
 \tikz[baseline=6pt]{\draw (0,0)rectangle(0.9,0.9);}
}

\newcommand{\ASF}[1] % Answer Space Filled
{%
 \tikz[baseline=6pt]{\draw (0,0)rectangle(0.9,0.9); \node at (0.45,0.35) {\color{blue}#1};}
}

\newcommand{\OneEquation}
{%
 \large
 \InitVariables
 \ifcase\Structure\relax
  \def\Exercise{\(\FactorA \times \FactorB = \AnswerSpace\)}
  \edef\Answer{\noexpand\(\FactorA \times \FactorB =\noexpand\ASF{\Product}\noexpand\)}
  \or
  \def\Exercise{\(\FactorA \times \AnswerSpace = \Product\)}
  \edef\Answer{{\noexpand\(\FactorA \times\noexpand \ASF{\FactorB} = \Product\noexpand\)}}
  \or
  \def\Exercise{\(\AnswerSpace \times \FactorB = \Product\)}
  \edef\Answer{\noexpand\(\noexpand\ASF{\FactorA} \times \FactorB = \Product\noexpand\)}
 \fi
}

\newcommand{\AnswerList}{}

\makeatletter
\newcommand{\ExerciseList}[1]
{%
 \foreach \x in {1,2,3,...,{#1}} 
 {%
  \OneEquation \Exercise \par \vspace{0.5cm}
   \expandafter\g@addto@macro\expandafter\AnswerList\expandafter
           {\Answer \par \vspace{0.5cm}}
 }
}
\makeatother




\begin{document}

\section{Exercises}
\ExerciseList{10}

\pagebreak
\section{Answers}
\AnswerList

\end{document}

enter image description here

  • Whoa. This will take me some time to grock. Will look more tonight and tomorrow. Thanks! – WeCanLearnAnything Oct 7 '16 at 23:18
  • How does one learn which commands can and can't go into \xdef and \edef? – WeCanLearnAnything Oct 8 '16 at 17:54
  • \edef isn't a documented latex command so formally no commands can. Otherwise, you need to check that it only works by expansion, so basically anything defined by anything more complicated than \newcommand\foo{hello world} will not work in edef. – David Carlisle Oct 8 '16 at 21:57
2

You're using \ASF in \xdef: this is not going to work, because \tikz is not an expandable command. It's not the only dangerous macro in that context.

Here's a different implementation using expl3. There's some code duplication, but it's possible you want to use different renderings for exercises and solutions, so I defined different functions for doing them.

\documentclass[twocolumn]{article}

\usepackage[margin=2cm]{geometry}
\usepackage{tikz}
\usepackage{xparse}

\setlength{\parindent}{0pt} 

\pgfmathsetseed{\number\pdfrandomseed}

\NewDocumentCommand{\InitVariables}{}{%
  \pgfmathsetmacro{\FactorA}{int(random(1,9))}%
  \pgfmathsetmacro{\FactorB}{int(random(1,9))}%
  \pgfmathsetmacro{\Product}{int(\FactorA*\FactorB)}%
  \pgfmathsetmacro{\Structure}{int(random(0,2))}%
}

\NewDocumentCommand{\AnswerSpace}{}{%
  \tikz[baseline=6pt]{\draw (0,0)rectangle(0.9,0.9);}%
}

\NewDocumentCommand{\ASF}{m}{% Answer Space Filled
  \tikz[baseline=6pt]{%
    \draw (0,0)rectangle(0.9,0.9);
    \node[blue] at (0.45,0.35) {#1};
  }%
}

\ExplSyntaxOn
\NewDocumentCommand{\OneEquation}{}
 {
  \InitVariables
  \int_case:nn { \Structure }
   {
    {0}{
        \wcla_make_exercise:xxx {\FactorA}{\FactorB}{\AnswerSpace}
        \wcla_make_answer:xxx {\FactorA}{\FactorB}{\ASF{\Product}}
       }
    {1}{
        \wcla_make_exercise:xxx {\FactorA}{\AnswerSpace}{\Product}
        \wcla_make_answer:xxx {\FactorA}{\ASF{\FactorB}}{\Product}
       }
    {2}{
        \wcla_make_exercise:xxx {\AnswerSpace}{\FactorB}{\Product}
        \wcla_make_answer:xxx {\ASF{\FactorA}}{\FactorB}{\Product}
       }
   }
}

\seq_new:N \l_wcla_exercise_list_seq
\seq_new:N \l_wcla_answer_list_seq

\cs_new_protected:Nn \wcla_make_exercise:nnn
 {
  \seq_put_right:Nn \l_wcla_exercise_list_seq { \( #1 \times #2 = #3 \) }
 }
\cs_generate_variant:Nn \wcla_make_exercise:nnn { xxx }

\cs_new_protected:Nn \wcla_make_answer:nnn
 {
  \seq_put_right:Nn \l_wcla_answer_list_seq { \( #1 \times #2 = #3 \) }
 }
\cs_generate_variant:Nn \wcla_make_answer:nnn { xxx }

\NewDocumentCommand{\ExerciseList}{m}
 {
  \prg_replicate:nn { #1 } { \OneEquation }
  \seq_use:Nn \l_wcla_exercise_list_seq { \par \vspace{0.5cm} }
 }

\NewDocumentCommand{\PrintAnswers}{}
 {
  \seq_use:Nn \l_wcla_answer_list_seq { \par \vspace{0.5cm} }
 }
\ExplSyntaxOff

\begin{document}

\section{Exercises}
\ExerciseList{10}

\newpage

\section{Answers}
\PrintAnswers

\end{document}

I used twocolumn just for getting everything in the same picture, for better checking.

The most notable thing is how \wcla_make_exercise:xxx is defined. First I define \wcla_make_exercise:nnn to add to the sequence containing the list of exercises the formula \(#1\times#2=#3\). Then the variant is defined that fully expands its three arguments and this is called so the values of \FactorA, \FactorB and \Product are the current ones. There is no problem in fully expanding \ASF here, because \NewDocumentCommand makes it robust.

enter image description here

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.