Take the 2-minute tour ×
TeX - LaTeX Stack Exchange is a question and answer site for users of TeX, LaTeX, ConTeXt, and related typesetting systems. It's 100% free, no registration required.

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.

share|improve this question
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. –  Dominikus K. Mar 8 '13 at 15:32
    
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. –  Sean Allred Mar 8 '13 at 15:51
2  
@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 '13 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 '13 at 17:32
1  
Did you check the extract package? –  mbork Mar 8 '13 at 22:19
show 4 more comments

5 Answers

up vote 15 down vote accepted

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

share|improve this answer
    
¡Muchas gracias! It works. Now I have a perfect excuse to learn LuaLaTeX. –  Ignasi Mar 11 '13 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 '13 at 15:35
add comment

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.

share|improve this answer
add comment

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 http://stackoverflow.com/questions/15329323/here-document-that-disables-shell-parsing for the here-document syntax to disable shell interpretation.

share|improve this answer
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!) –  Hendrik Vogt Mar 8 '13 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. –  Sean Allred Mar 8 '13 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 '13 at 0:49
    
Also $ in the tex could cause trouble. Imagine the document contains the formula $PWD$. –  JLDiaz Mar 9 '13 at 1:36
    
@JLDiaz With a here-document quotes work and $$ half works. Any suggestions? –  Ethan Bolker Mar 9 '13 at 21:39
show 3 more comments

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'
share|improve this answer
add comment

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}
share|improve this answer
    
I saw the reference to extract package in Extracting the contents of text in a specified environment into a new file just after sending my question. So, thank you for providing the example and pointing the problem with nested environments. –  Ignasi Mar 11 '13 at 15:02
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.