4

Speaking with Clemens (exsheets maintainer) he pointed me to a workaround by Gonzalo Medina, that helps put verbatim and listings code in the exsheets pseudo (Clemens' word choice) question and solution environments.

Fixing lstlisting inside exsheets question and solution environments

My question pertains to the repeatable sections in the solution to the previous link. Seeing the solution triggers the DRY (Don't repeat yourself) rules from my programming background and makes me want to write functional replacements for reusable sections.

How do I go about extracting the common code, so that I have a new function call (for lack of better phrasing), that I can reuse consistently, without replicating (read: copy pasting) the minibox code every time?

A MWE (modified from the mentioned question) to have a different box for question and solution.

\documentclass{article}

\usepackage{xcolor}
\usepackage{listings}
\usepackage{exsheets}

\lstset{
frame=single,
xleftmargin=20pt,
numbers=left,
numberstyle=\small,
tabsize=2,
breaklines,
showspaces=false,
showstringspaces=false,
language=C,
basicstyle=\small\ttfamily,
commentstyle=\itshape\color{gray}}


\newsavebox\myboxa
\newsavebox\myboxb

\begin{document}

\begin{lrbox}{\myboxa}\begin{minipage}{\textwidth}
\begin{lstlisting}[]
#include <stdio.h>

int main(int argc, char *argv[]) {
  printf("hello, world\n");
}
\end{lstlisting}
\end{minipage}
\end{lrbox}

\begin{lrbox}{\myboxa}\begin{minipage}{\textwidth}
\begin{lstlisting}[]
#include <stdio.h>

int main(int argc, char *argv[]) {
  printf("hello, world\n");
}
\end{lstlisting}
\end{minipage}
\end{lrbox}

\begin{lrbox}{\myboxb}\begin{minipage}{\textwidth}
\begin{lstlisting}[]
#include <stdio.h>

int main(int argc, char *argv[]) {
  printf("hello, world\n");
  # some more things that had to be done
}
\end{lstlisting}
\end{minipage}
\end{lrbox}


\begin{question}{6}
Pre-example text\par
\noindent\usebox\myboxa
\\Post-example text
\end{question}

\begin{solution}
Pre-solution text\par
\noindent\usebox\myboxb
\\Post-solution text
\end{solution}
\printsolutions 

\end{document}

This generates:

MWE example output

which would be much more usable, if I can extract the sections, indicated in overlay below:

Verbatim Extraction

If I can extract those sections and access them with something like:

\verbatimquestionsolution{6}{Preexample text}{Code}{Post-example text}{Pre-solution text}{Solution code}{Post-solution text}

or

\verbatimquestion{6}{Preexample text}{Code}{Post-example text}
\verbatimsolution{Preexample text}{Code}{Post-example text}

(with one's output immediately following other in output.

The pre and post solution text is not typically something I would use as one can do that in comments in the source, but is added here, for sake of discussing repeatable sections.

I know this might seem like a 'too specific' use case, but what I'm trying to learn here, is the extraction of sections (I'm not even really sure I'm using the right terminology here) so that I don't repeat myself. This won't just aid in this specific example but also with other repeatable environments I might encounter.

One thing I have no idea to approach for instance, is the

\newsavebox\myboxa
\newsavebox\myboxb

that comes in the preamble.

Any guidance as to this is much appreciated. What should I be reading up on here? Macros? How do I pass multiple variables? I see hooks are available but how do I get it to write to the preamble (if that is even necessary for the \newsavebox command)?

2 Answers2

4

Update 2013/10/29

Since version v0.10 (2013/10/24) exsheets bundles an new package, exsheets-listings, that has been build upon the answer proposed earlier. With it the syntax of two provided environments is a little different than with the code below:

\begin{lstquestion}[<options>]
<listing>
\end{lstquestion}

The <options> allow setting of text included before or after the listing and specifying the number of points of the question.

\documentclass{article}

\usepackage{exsheets}
\usepackage{exsheets-listings}

\lstdefinestyle{exercise}{
  frame=single,
  xleftmargin=20pt,
  numbers=left,
  numberstyle=\small,
  tabsize=2,
  breaklines,
  showspaces=false,
  showstringspaces=false,
  language=C,
  basicstyle=\small\ttfamily,
  commentstyle=\itshape\color{gray}
}
\SetupExSheets{
  question/listings={style=exercise} ,
  solution/listings={style=exercise}
}

\begin{document}

\begin{lstquestion}[
  pre    = {Pre-example text} ,
  post   = {Post-example text} ,
  points = 6]
#include <stdio.h>

int main(int argc, char *argv[]) {
  printf("hello, world\n");
}
\end{lstquestion}


\begin{lstsolution}[
  pre  = {Pre-solution-text} ,
  post = {Post-solution text}]
#include <stdio.h>

int main(int argc, char *argv[]) {
  printf("hello, world\n");
  # some more things that had to be done
}
\end{lstsolution}

\printsolutions 

\end{document}

enter image description here


Initial Answer

Here is a suggestion. The code is open for improvements (I'm a bit short of time right now... if I find the time I'll probably use it as a basis for an extension to exsheets).

Below I provide the code for a package exsheets-listings. It defines two environements:

\begin{lstquestion}[<options>]{<pre>}{<post>}{<points>}
<listing>
\end{lstquestion}

and

\begin{lstsolution}[<options>]{<pre>}{<post>}
<listing>
\end{lstsolution}

They write the listing into an auxiliary file, one for each question and each solution, which is then input internally via \lstinputlisting. This MWE

\documentclass{article}

\usepackage{exsheets}
\usepackage{exsheets-listings}

\begin{document}

\begin{lstquestion}{Pre-example text}{Post-example text}{6}
#include <stdio.h>

int main(int argc, char *argv[]) {
  printf("hello, world\n");
}
\end{lstquestion}


\begin{lstsolution}{Pre-solution-text}{Post-solution text}
#include <stdio.h>

int main(int argc, char *argv[]) {
  printf("hello, world\n");
  # some more things that had to be done
}
\end{lstsolution}

\printsolutions 

\end{document}

Save the following code as exsheets-listings.sty somewhere TeX can find it (in your working folder would do for a start):

\ProvidesPackage{exsheets-listings.sty}[2013/09/18 v0.1 listings in exsheets]
\RequirePackage{listings,xcolor,exsheets}

\lstdefinestyle{exercise}{
  frame=single,
  xleftmargin=20pt,
  numbers=left,
  numberstyle=\small,
  tabsize=2,
  breaklines,
  showspaces=false,
  showstringspaces=false,
  language=C,
  basicstyle=\small\ttfamily,
  commentstyle=\itshape\color{gray}
}

\ExplSyntaxOn
\cs_new:Npn \lst_question_expandopt:Nnnnn #1#2#3#4#5
  { #1{#2}[#3]{#4}#5 }
\cs_generate_variant:Nn \lst_question_expandopt:Nnnnn { NnVnx }
\cs_new_eq:NN \lst@question@expandoption \lst_question_expandopt:NnVnx

\cs_new:Npn \lst_question_expand_opt_and_body:Nnnn #1#2#3#4
  { #1{#2}[#3]#4 }
\cs_generate_variant:Nn \lst_question_expand_opt_and_body:Nnnn { NnVx }
\cs_new_eq:NN
  \lst@question@expand@option@and@body
  \lst_question_expand_opt_and_body:NnVx
\ExplSyntaxOff

\lst@RequireAspects{writefile}

\newsavebox\lst@question@box

\newcounter{questionlisting}

\lstnewenvironment{lstquestion}[4][]{%
  \def\lst@question@pre{#2}%
  \def\lst@question@post{#3}%
  \def\lst@question@points{#4}%
  \def\lst@question@options{#1}%
  \stepcounter{questionlisting}%
  \setbox\lst@question@box=\hbox\bgroup
    \lst@BeginAlsoWriteFile{\jobname-ex\thequestionlisting.lst}
}
{%
    \lst@EndWriteFile
  \egroup
  \lst@question@expandoption
  \begin{question}\lst@question@options{\lst@question@points}{%
    \lst@question@pre
    \noexpand\lstinputlisting[style=exercise]
      {\jobname-ex\thequestionlisting.lst}%
    \lst@question@post
  }%
  \end{question}%
}

\newcounter{solutionlisting}

\lstnewenvironment{lstsolution}[3][]{%
  \def\lst@question@pre{#2}%
  \def\lst@question@post{#3}%
  \def\lst@question@options{#1}%
  \stepcounter{solutionlisting}%
  \setbox\lst@question@box=\vbox\bgroup
    \lst@BeginAlsoWriteFile{\jobname-sol\thesolutionlisting.lst}
}
{%
    \lst@EndWriteFile
  \egroup
  \lst@question@expand@option@and@body
  \begin{solution}\lst@question@options{%
    \expandonce\lst@question@pre
    \noexpand\lstinputlisting[style=exercise]
      {\jobname-sol\thesolutionlisting.lst}%
    \expandonce\lst@question@post
  }%
  \end{solution}%
}
\endinput
cgnieder
  • 66,645
  • 1
    I really appreciate the time and effort you put into this answer. From the little I've learnt this past month, I'm gathering that the code I'm seeing past the lstdefinestyle section is expl3? Is that code section considered a macro? I'll need quite a bit of time to figure this out line by line, the syntax still seems very unintuitive to me at this point. – Forkrul Assail Sep 19 '13 at 04:33
  • 1
    Thw part between \ExplSyntaxOn and \ExplSyntaxOff is expl3. I've used it because it makes expansion control way easier and I needed the bodies of the question and the solution environments expanded. – cgnieder Sep 19 '13 at 09:04
  • 1
    @ForkrulAssail I'll release v0.10 in the next few days. It will contain a sub-package exsheets-listings that builds on the approach above but uses a key/value syntax for adding pre/post stuff. – cgnieder Sep 27 '13 at 12:09
  • and I will need an address to aid in the procurement and distribution of some fine SA wines to the author of said package. – Forkrul Assail Sep 27 '13 at 16:06
  • @ForkrulAssail :) Well, you know how to get in contact with me... – cgnieder Sep 27 '13 at 19:22
  • I think there might be a small bug, in that everything works until I specify options after which I get Undefined control sequence \end{lstquestion}. Or maybe I just put the options in the wrong sequence? – Forkrul Assail Sep 30 '13 at 14:29
  • @ForkrulAssail How are you specifying the options? (And which ones?) – cgnieder Sep 30 '13 at 14:37
3

If I were doing something like this in ConTeXt, I would use buffers to store verbatim text, and use a key-value driven interface to specify the pre and post arguments. The gist of the following code should be understandable even if you don't use ConTeXt.

\definenamespace
  [exercisesolution]
  [
    name=exercisesolution,
    setup=yes,
  ]

\defineenumeration[exercise][text=Exericse] 
\defineenumeration[solution][text=Solution]

\define\useexercisesolution
    {\dosingleargument\dodefineexercisesolution}

\def\dodefineexercisesolution[#1]%
    {\setupexercisesolution[#1]%
     \startexercise
       \exercisesolutionparameter{exercisebefore}%
       \typebuffer[\exercisesolutionparameter{bufferbefore},
                   \exercisesolutionparameter{bufferexercise},
                   \exercisesolutionparameter{bufferafter}]%
      \exercisesolutionparameter{exerciseafter}%
    \stopexercise
    \startsolution
       \exercisesolutionparameter{solutionbefore}%
       \typebuffer[\exercisesolutionparameter{bufferbefore},
                   \exercisesolutionparameter{bufferexercise},
                   \exercisesolutionparameter{buffersolution},
                   \exercisesolutionparameter{bufferafter}]%
      \exercisesolutionparameter{solutionafter}%
    \stopsolution}


\startbuffer[C-header]
#include <stdio.h>

int main(int argc, char *argv[]) {
\stopbuffer

\startbuffer[C-footer]
}
\stopbuffer

% Common setup for all questions
\setupexercisesolution
  [bufferbefore=C-header,
   bufferafter=C-footer]

\starttext

\startbuffer[question]
    printf("Hello World\n");
\stopbuffer

\startbuffer[answer]
    # Some more things that need to be done
    # and then more, as this is the solution
\stopbuffer

\useexercisesolution
  [
    exercisebefore={Pre example text},
    exerciseafter={Post example text},
    solutionbefore={Pre solution text},
    solutionafter={Post solution text},
    bufferexercise=question,
    buffersolution=answer,
  ]

\stoptext

which gives

enter image description here

Note that I am also reusing the boilerplate code, and assuming that the code presented in the exercise will always be repeated in the solution.

It is possible to add syntax highlighting (vim module) and frames, but I omitted them so that it was easier to see how the content is being reused.

Aditya
  • 62,301