35

When I prepare an exercise list for my students I use to also include there my own solutions. I prepare these documents with some macros based on mdframed and with comment help I easily obtain two pdfs, one with exercises and another one with exercises and solutions.

I encourage my students to try to write their solutions with LaTeX so I give them the .pdf file with exercises and a source .tex file without answers. This way, they only have to worry about writing their solutions because the format is already provided. The problem is that I need to have two source files one with problems+solutions and another one only with problems.

What I would like would be to have only one .tex file with problems and solutions and, from it be able to extract another .tex file without solutions. As a possible frame work suppose a main .tex file like:

% This is main.tex file
\documentclass{article}
\begin{document}
\begin{exercise} 
This is the first exercise
\begin{solution}
This is my solution
\end{solution}
\end{exercise}
\end{document}

from which I want to easily obtain a similar one with empty solutions:

% This is student.tex file
\documentclass{article}
\begin{document}
\begin{exercise} 
This is the first exercise
\begin{solution}
\end{solution}
\end{exercise}
\end{document}

I suppose I'm looking for something like a .dtx file but I've never used it and may be there are better solutions. I'm working on windows so grep commands won't work.

10
  • 1
    I would write a short parser, which just cut out the text between \begin{solution} and \end{solution}. But I am not sure whether an analog is possible through LaTeX itself, too. Mar 8, 2013 at 15:32
  • 1
    Have you considered the exam document class? It won't give you two tex sources, but as @DominikusK. says, you can easily write a parser. Mar 8, 2013 at 15:51
  • 3
    @JLDiaz: No. I do need two .tex files because I want to give one to the students. I know how to get different pdf from same file.
    – Ignasi
    Mar 8, 2013 at 17:30
  • @SeanAllred I know exam, exsheets, and some other classes for exercises. With them i can produce different pdfs but i want different source (.tex) files.
    – Ignasi
    Mar 8, 2013 at 17:32
  • 1
    Did you check the extract package?
    – mbork
    Mar 8, 2013 at 22:19

6 Answers 6

19

Update.

Added support to leave the empty environment in the copy, removing only its contents, or to remove also the \begin...\end pair (by default).


I programmed a LuaLaTeX solution and tried to make it flexible enough. These are the files which compose the solution:

remove-env.lua

-- remove-env.lua
omittedEnvironments = {}
omitFileSuffix = "-without"
leaveEmptyEnvs = false

function shouldOmit(line)
  for i,v in ipairs(omittedEnvironments) do
    if (string.find(line, "\\begin{"..v.."}")~=nil) then
       return true
    end
  end
  return false
end

function shouldResume(line)
  for i,v in ipairs(omittedEnvironments) do
    if (string.find(line, "\\end{"..v.."}")~=nil) then
       return true
    end
  end
  return false
end

function dumpfile()
    myout = io.open(tex.jobname..omitFileSuffix..".tex", "w")
    myin = io.open(tex.jobname..".tex", "r")
    omitting = false
    for line in myin:lines() do
      if (not omitting and shouldOmit(line)) then
          if (leaveEmptyEnvs) then myout:write(line.."\n") end
          omitting = true
      end
      if (not omitting) then
         myout:write(line.."\n")
      end
      if (omitting and shouldResume(line)) then
          if (leaveEmptyEnvs) then myout:write(line.."\n") end
          omitting = false
      end
    end
    myout:close()
    myin:close()
end

remove-env.tex

\directlua{dofile("remove-env.lua")}
\def\omitEnvironment#1{\directlua{table.insert(omittedEnvironments, "#1")}}
\def\omitFileSuffix#1{\directlua{omitFileSuffix="#1"}}
\def\leaveEmptyEnvs{\directlua{leaveEmptyEnvs=true}}
\def\removeEmptyEnvs{\directlua{leaveEmptyEnvs=false}}
\AtEndDocument{\directlua{dumpfile()}}

MWE.tex

\input remove-env
\documentclass{article}
\usepackage{fontspec}
\usepackage{lipsum}
\newenvironment{solution}{}{}
\omitEnvironment{solution}
\omitFileSuffix{-sans-sol}

\begin{document}\parindent0pt\parskip1em
  1. \lipsum[1]\hrulefill\par
  \begin{solution}
  2. \lipsum[2]\hrulefill\par
  \end{solution}
  3. \lipsum[3]\hrulefill\par
\end{document}

This MWE defines a no-op solution environment which acts simply as markup, but of course you can define it in a way that produces some effect in the pdf. Macro \omitEnvironment specifies the environment you want to omit. You can use this macro several times to specify several environments, and all of them will be omitted. Macro \omitFileSuffix specifies the suffix that will be appended to the output filename.

Run:

$ lualatex MWE.tex

And you will get two files (and all the usual auxiliar files, of course):

  • MWE.pdf will be generated as usually, and all the contents (including omitted environments) will be present.
  • MWE-sans-sol.tex is a copy of MWE.tex in which all solution environments are removed.
$ diff MWE.tex MWE-sans-sol.tex
11,13d10
<   \begin{solution}
<   2. \lipsum[2]\hrulefill\par
<   \end{solution}

If you want to remove only the contents of the solution but leave the empty environment, you only have to specify \leaveEmptyEnvs at some point of MWE.tex. In this case the diff will show:

$ diff MWE.tex MWE-sans-sol.tex
12d11
<   2. \lipsum[2]\hrulefill\par

PS: Thanks to Scott H. who suggested me not to use luatex callbacks, which was my first (and too convoluted) approach

2
  • ¡Muchas gracias! It works. Now I have a perfect excuse to learn LuaLaTeX.
    – Ignasi
    Mar 11, 2013 at 15:07
  • My first lualatex lesson: changing \directlua{dofile("remove-env.lua")} to \directlua{require("remove-env.lua")} I can have both remove-env files in my localtex folder instead of working folder.
    – Ignasi
    Mar 11, 2013 at 15:35
15

A TeX only solution; it assumes that you don't have any other environment whose name starts with the string soluti other than solution.

Prepare the following extract.tex file:

\newread\texfileread
\newwrite\texfilewrite
\openin\texfileread=ignasimain.tex % put here the main file name
\immediate\openout\texfilewrite=ignasistudents.tex % put here the secondary file name

\edef\BEGINSOLUTI{\string\begin\string{soluti}
\edef\ENDSOLUTION{\string\end\string{solution}
\newif\ifwritesolution
\writesolutiontrue

\long\def\ignasidecide#1#2#3#4#5#6#7#8#9\relax{%
  \def\temp{#1#2#3#4#5#6#7#8}%
  \ignasidecideaux#9}
\long\def\ignasidecideaux#1#2#3#4#5#6\relax{%
  \ifnum\pdfstrcmp{\temp#1#2#3#4#5}{\BEGINSOLUTI}=0
    \immediate\write\texfilewrite{\ignasiline^^J}
    \writesolutionfalse
  \else
    \ifnum\pdfstrcmp{\temp#1#2#3#4#5}{\ENDSOLUTION}=0
      \writesolutiontrue
    \fi
  \fi
  \ifwritesolution
    \immediate\write\texfilewrite{\ignasiline}
  \fi
}

\endlinechar=-1 \newlinechar=`\^^J
\loop\unless\ifeof\texfileread%
\readline\texfileread to \ignasiline%
\expandafter\ignasidecide\ignasiline%
  \relax\relax\relax\relax%
  \relax\relax\relax\relax%
  \relax\relax\relax\relax%
  \relax\relax%
\repeat%
\csname bye\endcsname%
\csname @@end\endcsname

Change the file names as desired. Put this file along with the main file and compile with pdftex or pdflatex (it's the same).

With your example file, the resulting file will be

% This is main.tex file
\documentclass{article}
\begin{document}
\begin{exercise}
This is the first exercise
\begin{solution}

\end{solution}
\end{exercise}
\end{document}

Basically we read the main file line by line (ignoring category codes) with \readline; if the line starts with

\begin{soluti

then we write out the line with a blank line following it and set a conditional to false; if the line starts with

\end{solution

then the conditional is set again to true. The current line is written out if the conditional is true.

11

I had the same problem. I ended up using docstrip.

The system I set up produces:

  • (i) separate tex and pdf files for me (with solutions) and the students (without solutions:),

  • (ii) several versions of the exercise, for grouping students, and

  • (iii) typesets the only selected topic(s), allowing me to keep all the exercises in the same file.

Here's the outline of the main file containing exercises and solutions:

% This is exercises.tex
\documentclass{article}
\newenvironment{exercise}{}{}
\newenvironment{solution}{}{}

\title{Subject\\\normalsize homework topic:
%<topic1>topic 1
%<topic2>topic 2
}
\author{}

\begin{document}
\maketitle

%<*topic1>
\begin{exercise}
  Exercise text (topic 1)
%<A>  Version for group A,
%<B>  Version for group B,
%<C>  Version for group C,
%<D>  Version for group D.
  More exercise text.
\end{exercise}
%<*!student>
\begin{solution}
  Solution, visible only in my copy:
%<A> for group A,
%<B> for group B,
%<C> for group C,
%<D> for group D.
\end{solution}
%</!student>

%<*student>
% Template for student's answer.
\begin{solution}

\end{solution}
%</student>

%</topic1>



%<*topic2>
\begin{exercise}
  Exercise text (topic 1)
%<A>  Version for group A,
%<B>  Version for group B,
%<C>  Version for group C,
%<D>  Version for group D.
  More exercise text.
\end{exercise}
%<*!student>
\begin{solution}
  Solution, visible only in my copy:
%<A> for group A,
%<B> for group B,
%<C> for group C,
%<D> for group D.
\end{solution}
%</!student>

%<*student>
% Template for student's answer.
\begin{solution}

\end{solution}
%</student>

%</topic2>

\end{document}

And this is the .ins file for topic1:

% This is exercises.ins
\input docstrip
\nopreamble\nopostamble
\askforoverwritefalse
\generate{%
  \file{exercises-topic1-A.tex}{\from{exercises.tex}{student,topic1,A}}%
  \file{exercises-topic1-B.tex}{\from{exercises.tex}{student,topic1,B}}%
  \file{exercises-topic1-C.tex}{\from{exercises.tex}{student,topic1,C}}%
  \file{exercises-topic1-D.tex}{\from{exercises.tex}{student,topic1,D}}%
  \file{exercises-topic1-ME.tex}{\from{exercises.tex}{topic1,A,B,C,D}}%
  %\file{exercises-topic2-A.tex}{\from{exercises.tex}{student,topic2,A}}%
  %\file{exercises-topic2-B.tex}{\from{exercises.tex}{student,topic2,B}}%
  %\file{exercises-topic2-C.tex}{\from{exercises.tex}{student,topic2,C}}%
  %\file{exercises-topic2-D.tex}{\from{exercises.tex}{student,topic2,D}}%
  %\file{exercises-topic2-ME.tex}{\from{exercises.tex}{topic2,A,B,C,D}}%
}
\endbatchfile

Finally, a very badly written makefile --- nothing more than a camouflaged bash script, really. (I guess that ideally, running make should produce the .ins file automatically ... ahh, some day...)

all:
    pdftex exercises.ins
    bash -c 'for I in {A,B,C,D,ME}; do pdflatex exercises-topic1-$$I ; done'
    #bash -c 'for I in {A,B,C,D,ME}; do pdflatex exercises-topic2-$$I ; done'
10

Here's an awk program that generates a TeX file with everything but the contents of solution environments, leaving those environments for students to fill in. A hack for windows follows.

#!/usr/bin/awk
# Filter out solution environment
#

BEGIN{
    printing = 1;
}

/begin\{solution/ {
    print 
    print "  write your answer here"
    printing = 0;
}

printing >0 {
    print;
}

/end\{solution/ {
    print 
    printing = 1;
}

@HendrikVogt notes that you're on windows. This might work for you: http://gnuwin32.sourceforge.net/packages/gawk.htm

Edit: A clumsy windows solution. It uses an online bash shell to save a copy of the awk program, save a copy of the TeX source, then execute the program on the source.

IMPERFECTION: solution seems to eat white space at the beginning of a line. TeX wouldn't care, but users might.

cat << 'EOF' > /tmp/myprog 
#!/usr/bin/awk
BEGIN{  printing = 1;}
/begin\{solution/ { print; print "your answer  here"; printing = 0;}
printing >0 { print}
/end\{solution/ { print; printing = 1;}
EOF
#
# paste your TeX document here
cat << 'EOF' > /tmp/mytex 
\documentclass{article}
\begin{document}
Test mathematics:  $e^{i\pi} = -1$.
Test "double" and 'single' quotes and a *.
\begin{exercise}
    Question here: $ 2 + 2 = ?$.
\begin{solution}
    $4$
\end{solution}
\end{exercise}
\end{document}
EOF
awk -f /tmp/myprog /tmp/mytex

Output for this test:

enter image description here

Thanks to https://stackoverflow.com/questions/15329323/here-document-that-disables-shell-parsing for the here-document syntax to disable shell interpretation.

8
  • 4
    Please note that the OP works on Windows and doesn't look for a solution with grep and friends. (On a Unix system I'd use such an external program, too!) Mar 8, 2013 at 15:59
  • If I had access to a Windows machine, I would advise that VBS be looked into as an alternative. Installing open ports is a pain on Windows, and it's much easier to just use the built-in tools (even if far inferior) for single-purpose things like this. Mar 8, 2013 at 20:43
  • 1
    @HendrikVogt: grep is available for Windows. There's also CygWin that provides a Unix-like interface and accompanying command-line programming functionality, including grep.
    – Werner
    Mar 9, 2013 at 0:49
  • Also $ in the tex could cause trouble. Imagine the document contains the formula $PWD$.
    – JLDiaz
    Mar 9, 2013 at 1:36
  • @JLDiaz With a here-document quotes work and $$ half works. Any suggestions? Mar 9, 2013 at 21:39
3

The simplest option is probably the extract package, mentioned by mbork in comments. However, this method will not allow you to nest your solution environment inside the exercise environment.

\documentclass{article}
\usepackage[
  active,
  header=false,
  copydocumentclass=true,
  generate=\jobname-no-solutions,
  extract-env={exercise}
  ]{extract} % http://ctan.org/pkg/extract
\begin{extract*}
% Items executed in both the main and extracted document
% (extract manual, section 5.1)
\newenvironment{exercise}{}{}
\newenvironment{solution}{}{}
\end{extract*}
\begin{document}
\begin{exercise} 
This is the first exercise
\end{exercise}
\begin{solution}
This is my solution
\end{solution}
\end{document}

Resulting in code for the extracted file of:

\documentclass{article}
% Items executed in both the main and extracted document
% (extract manual, section 5.1)
\newenvironment{exercise}{}{}
\newenvironment{solution}{}{}

\begin{document}

\begin{exercise}
This is the first exercise
\end{exercise}

\end{document}
1
2

I have been using answers package for this purpose. You can prepare different versions of solutions also if you want, a marking scheme, a full solution, all from the same file. The documentation has examples. Here is how I use it.

\documentclass{article}
\usepackage{answers}%Use this once the solutions are final.
%\usepackage[nosolutionfiles]{answers}%Use this while preparing solutions.%
\Newassociation{sol}{nothing}{ans}
\newcommand{\nothingparams}{\relax}
\begin{document}
\Opensolutionfile{ans}[mysolution-file]
\begin{Filesave}{ans}
\begin{center}
%details regarding examination etc.
\end{center}
\begin{enumerate}
\end{Filesave}
\begin{exercise}
......
\end{exercise}
\begin{sol}
.......
\end{sol}
....


\begin{Filesave}{ans}
    \end{enumerate}
    \end{Filesave}
    \Closesolutionfile{ans}
\newpage
     %\input{mysolution-file.tex}%Remove the comment after finalising.
    \end{document}

Explanation: Everything you include between \begin{sol} and \end{sol} is written to the file mysolution-file.tex. If you choose the no solutionfiles option, the solution is printed right below the question. This is convenient while preparing questions. After finalising you can disable this line and enable the line without the option. You can also enable the \input command in the end. All the solutions are printed after the question paper. Everything between \begin{Filesave}{ans} and \end{Filesave} is saved to the file mysolution-file.tex. I use this to write information regarding the examination to the solution file. I also use them to enclose the solution in enumerate environment. Alternatively, you can write another main tex file and \include the mysolution-file.tex.

Edit I read the question again. You need a texfile with the solutions removed also. You can use extract package to produce a solution file with the portions enclosed between \begin{sol} and \end{sol} removed.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .