6

I am designing a set of macros to have exercises in a book and write the answers at the end while typesetting the answers just after the questions in the exercise file. (See MWE for an example). (The idea is to be able to renew the command \answer and print the answers right after the questions, but that another story).

I adapted the code of the package endnotes and use basically two commands for the answer \writesolutions and \appendsolutions that differ in the way they process fragile commands. The answers are written in a output file that is included using the command \printsolutions. Somehow, the output file is cut at the 16384th character. In the following MWE, the chapter of Problems expands when we add more lines of \input ex1 but after 27 such lines, the solution chapter does not expand anymore and we get some errors.

EDIT

The content of the file mwe.exo is not clear to me : after the run, it only contains, as noted by David Carlisle, \begin{questions} and \end{questions}, whereas it should contain the code of all answers. I don't understand this, but I suspect the issue is related to the truncation of the file. Here's how I determined the length of the answers's code that is truncated : I have defined a command

\def\abcdefghijklmnopqrstuvwxyz{\relax}

and if I insert it at the right position, I get errors as Undefined control sequence for \abcdefghijklmn. I know the length of one answer using also error messages. Note that with other content I get the same error with the token \addtoco or any other know command name cut in the middle, but then I can't count the number of characters.

This problem is far beyond my knowledge. Thanks to all who whould help !

MWE

\documentclass{book}
\usepackage{filecontents}
\usepackage{xkeyval}
\usepackage{etoolbox}
\usepackage{enumitem}

% File management adapted from endnotes.sty ©2002 John Lavagnino
\makeatletter
\newwrite\@exsol
\newif\if@exsolopen
\global\@exsolopenfalse

\def\@openexsol{\immediate\openout\@exsol=\jobname.exo\relax
      \global\@exsolopentrue}

% writesolutions does not expand fragile commands
% \strip@prefix trick given by egreg on TeX.SE (2016/11/09)
% http://tex.stackexchange.com/questions/338302/what-is-the-meaning-of-meaning
\long\def\writesolutions#1{%
     \if@exsolopen \else \@openexsol \fi
     \begingroup
        \def\solution{#1}%
        \newlinechar='40
        \immediate\write\@exsol{\expandafter\strip@prefix\meaning\solution}%
     \endgroup}

% \appendsolutions expands fragile commands first 
\long\def\appendsolutions#1{%
     \if@exsolopen \else \@openexsol \fi
     \begingroup
        \newlinechar='40
        \let\protect\string
        \immediate\write\@exsol{#1}%
     \endgroup}

% printsolutions
\long\def\printsolutions{
  \immediate\closeout\@exsol \global\@exsolopenfalse
  \chapter{Solutions}
  \input{\jobname.exo}}
% Settings for probleme environment in a book document
\newcounter{probleme}[chapter]
\def\theprobleme{\arabic{probleme}}
\def\problemename{Problem}

\newlist{questions}{enumerate}{10}
\setlist[questions]{label=\textit{\alph*}),ref=\textit{\alph*}}

\define@cmdkey[PRE]{problem}{title}{}
\presetkeys[PRE]{problem}{title=Problem}{}


\newenvironment{probleme}[1][]{
  \setkeys[PRE]{problem}{#1}
  \refstepcounter{probleme}
  \appendsolutions{%
     {\problemename~\theprobleme\, (page \thepage)}}
  \par\noindent\textbf{\theprobleme. \cmdPRE@problem@title}
}
{}

\newcommand\answer[1]{\writesolutions{\par #1}}
\newcommand\noanswer{\relax}

\AtBeginEnvironment{questions}{%
  \writesolutions{\begin{questions}}%
  \renewcommand\answer[1]{\writesolutions{\item{#1}}}%
  \renewcommand\noanswer{\writesolutions{\addtocounter{questionsi}{1}}}
}
\AtEndEnvironment{questions}{%
  \writesolutions{\end{questions}}%
  \renewcommand\answer[1]{\par #1}
  \renewcommand\noanswer{\relax}
}
\makeatother

\begin{document}
\begin{filecontents*}{ex1.tex}
\begin{probleme}[title=Problem]
\begin{questions}
\item 1+1 \answer{2}
\item 2+2 \noanswer
\item 3+3 \answer{6}
\item 4+4
\end{questions}
\end{probleme}
\end{filecontents*}

\mainmatter
\chapter{Problems}
\input ex1
\input ex1
\input ex1 
% Add as many similar lines as needed. 
% On my computer the error appears when ex1.tex 
% is included 27 times.

\backmatter
\printsolutions
\end{document}
  • 2
    I get no error if I have hundred \input ex1` lines but the .exo file just have \begin{questions}\end{questions} repeated over and over and no content? – David Carlisle Nov 10 '16 at 16:07
  • @DavidCarlisle. I have actually the same behaviour, except that the solution chapter is cut as if the exo file was damaged after the 16384th character (it seems to machine dependent). I could get this number by inserting some dummy token like \abcdefghijklmnopqrsuvwxyz between \input lines. – Tom-Tom Nov 10 '16 at 17:01
  • something very odd just tried it with 341 \input ex1 and get no error in texlive 2016 only has 12276 characters though which does not match your statement about 16384 characters which is why i ased if it should just have empty environments – David Carlisle Nov 10 '16 at 18:11
  • do you know answers – touhami Nov 14 '16 at 9:52
6
+100

I think that the problem is in \printsolutions. It inputs the file \jobname.exo which contains blocs like

{Problem\nobreakspace{}1\,(page 1)}
\begin{questions}
\item{2}
\addtocounter{questionsi}{1}
\item{6}
\end{questions}

Now, \begin{questions} writes to \jobname.exo which means that you write to the file you are reading. What seems to be a size limitation is probably just a matter of buffering of the read operation. I guess that the command \immediate\openout\@exsol=\jobname.exo destroys the content of \jobname.exo but a block has already been read and put in memory. When the read buffer is empty, a new block is read from the file, but at this time the file has changed.

By the way, the write operation is not completely correct either. The blocks written to the file actually look like

{Problem\nobreakspace
{}2\,
(page
1)}
\begin
{questions}
\item
{2}
\addtocounter
{questionsi}{1}
\item
{6}
\end
{questions}

It would at least require a % after \,

2

You're using the same environment for questions and solutions; unless your \printsolutions macro redefines questions, this environment will again write to the .exo file.

Just use a different name.

\documentclass{book}
\usepackage{filecontents}
\usepackage{xkeyval}
\usepackage{etoolbox}
\usepackage{enumitem}

% File management adapted from endnotes.sty ©2002 John Lavagnino
\makeatletter
\newwrite\@exsol
\newif\if@exsolopen
\global\@exsolopenfalse

\def\@openexsol{\immediate\openout\@exsol=\jobname.exo\relax
      \global\@exsolopentrue}

% writesolutions does not expand fragile commands
% \strip@prefix trick given by egreg on TeX.SE (2016/11/09)
% http://tex.stackexchange.com/questions/338302/what-is-the-meaning-of-meaning
\long\def\writesolutions#1{%
     \if@exsolopen \else \@openexsol \fi
     \begingroup
        \def\solution{#1}%
        \newlinechar='40
        \immediate\write\@exsol{\expandafter\strip@prefix\meaning\solution}%
     \endgroup}

% \appendsolutions expands fragile commands first 
\long\def\appendsolutions#1{%
     \if@exsolopen \else \@openexsol \fi
     \begingroup
        \newlinechar='40
        \let\protect\string
        \immediate\write\@exsol{#1}%
     \endgroup}

% printsolutions
\long\def\printsolutions{
  \immediate\closeout\@exsol \global\@exsolopenfalse
  \chapter{Solutions}
  \input{\jobname.exo}}
% Settings for probleme environment in a book document
\newcounter{probleme}[chapter]
\def\theprobleme{\arabic{probleme}}
\def\problemename{Problem}

\newlist{questions}{enumerate}{10}
\setlist[questions]{label=\textit{\alph*}),ref=\textit{\alph*}}

\define@cmdkey[PRE]{problem}{title}{}
\presetkeys[PRE]{problem}{title=Problem}{}


\newenvironment{probleme}[1][]{
  \setkeys[PRE]{problem}{#1}
  \refstepcounter{probleme}
  \appendsolutions{%
     {\problemename~\theprobleme\, (page \thepage)}}
  \par\noindent\textbf{\theprobleme. \cmdPRE@problem@title}
}
{}

\newcommand\answer[1]{\writesolutions{\par #1}}
\newcommand\noanswer{\relax}

\AtBeginEnvironment{questions}{%
  \writesolutions{\begin{questions}}%
  \renewcommand\answer[1]{\writesolutions{\item{#1}}}%
  \renewcommand\noanswer{\writesolutions{\addtocounter{questionsi}{1}}}
}
\AtEndEnvironment{questions}{%
  \writesolutions{\end{questions}}%
  \renewcommand\answer[1]{\par #1}
  \renewcommand\noanswer{\relax}
}
\makeatother

\begin{document}
\begin{filecontents*}{ex1.tex}
\begin{probleme}[title=Problem]
\begin{questions}
\item 1+1 \answer{2}
\item 2+2 \noanswer
\item 3+3 \answer{6}
\item 4+4
\end{questions}
\end{probleme}
\end{filecontents*}

\mainmatter
\chapter{Problems}
\input ex1
\input ex1
\input ex1 
% Add as many similar lines as needed. 
% On my computer the error appears when ex1.tex 
% is included 27 times.

\backmatter
\printsolutions
\end{document}

This is the contents of the .exo file:

{Problem\nobreakspace
{}1\,
(page
1)}
\begin
{solutions}
\item
{2}
\addtocounter
{questionsi}{1}
\item
{6}
\end
{solutions}
{Problem\nobreakspace
{}2\,
(page
1)}
\begin
{solutions}
\item
{2}
\addtocounter
{questionsi}{1}
\item
{6}
\end
{solutions}
{Problem\nobreakspace
{}3\,
(page
1)}
\begin
{solutions}
\item
{2}
\addtocounter
{questionsi}{1}
\item
{6}
\end
{solutions}

enter image description here

  • Thank you for spotting my stupid mistake ! Where did you change the name, actually ? I can't find it in the code. – Tom-Tom Nov 14 '16 at 19:57

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.