在与 Clemens(exsheets 维护者)交谈时,他向我介绍了 Gonzalo Medina 提出的一种解决方法,该方法有助于将逐字和列表代码放入 exsheets 中伪(克莱门斯的词语选择)问题和解决方案环境。
修复 exsheets 问题和解决方案环境中的 lstlisting
我的问题与上一个链接的解决方案中的可重复部分有关。看到解决方案触发了我编程背景中的 DRY(不要重复自己)规则,并让我想为可重复使用的部分编写功能性替代品。
我该如何提取通用代码,这样我就有了一个新的函数调用(缺乏更好的措辞),我可以始终如一地重复使用,而不必每次都复制(阅读:复制粘贴)迷你盒代码?
MWE(根据提到的问题修改而来)有一个用于问题和解决方案的不同的框。
\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}
这将生成:
如果我可以提取下面覆盖部分所示的部分,它将更加有用:
如果我可以提取这些部分并使用以下命令访问它们:
\verbatimquestionsolution{6}{Preexample text}{Code}{Post-example text}{Pre-solution text}{Solution code}{Post-solution text}
或者
\verbatimquestion{6}{Preexample text}{Code}{Post-example text}
\verbatimsolution{Preexample text}{Code}{Post-example text}
(其中一个的输出紧接着另一个的输出。
我通常不会使用解决方案前后的文本,因为人们可以在源代码中的注释中这样做,但为了讨论可重复的部分,我在这里添加了它。
我知道这可能看起来像是一个“过于具体”的用例,但我在这里试图学习的是提取部分(我甚至不确定我在这里使用的术语是否正确),这样我就不会重复自己。这不仅有助于这个特定的例子,也有助于我可能遇到的其他可重复的环境。
有一件事我不知道该怎么做,比如
\newsavebox\myboxa
\newsavebox\myboxb
这是序言中的内容。
任何有关这方面的指导都非常感谢。我应该在这里阅读什么?宏?如何传递多个变量?我看到钩子可用,但我如何让它写入前导码(如果这对于 \newsavebox 命令来说是必要的)?
答案1
更新 2013/10/29
自版本 v0.10 (2013/10/24) 以来,exsheets
捆绑了一个新包,exsheets-listings
该包是基于先前提出的答案构建的。使用它,两个提供的环境的语法与下面的代码略有不同:
\begin{lstquestion}[<options>]
<listing>
\end{lstquestion}
允许<options>
设置在列表之前或之后包含的文本并指定问题的点数。
\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}
初步答案
这是一条建议。代码可以改进(我现在时间有点紧……如果我有时间,我可能会用它作为扩展的基础exsheets
)。
下面我提供了一个包的代码exsheets-listings
。它定义了两个环境:
\begin{lstquestion}[<options>]{<pre>}{<post>}{<points>}
<listing>
\end{lstquestion}
和
\begin{lstsolution}[<options>]{<pre>}{<post>}
<listing>
\end{lstsolution}
他们将列表写入辅助文件,每个问题和每个解决方案一个,然后通过 进行内部输入\lstinputlisting
。这个 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}
将以下代码保存到exsheets-listings.sty
TeX 可以找到的地方(首先保存在您的工作文件夹中):
\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
答案2
如果我在 ConTeXt 中执行类似操作,我会使用缓冲区来存储逐字文本,并使用键值驱动接口来指定前置和后置参数。即使您不使用 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
这使
请注意,我也重复使用样板代码,并假设练习中提供的代码将始终在解决方案中重复。
可以添加语法高亮(vim 模块)和框架,但我省略了它们,以便更容易看到内容是如何被重复使用的。