当我为学生准备练习列表时,我也会在其中加入自己的解决方案。我根据宏准备了这些文档,mdframed
借助这些comment
宏,我轻松获得了两个 pdf,一个包含练习,另一个包含练习和解决方案。
我鼓励我的学生尝试用 编写解决方案,LaTeX
因此我给他们一个.pdf
包含练习的文件和一个.tex
不包含答案的源文件。这样,他们只需要担心编写解决方案,因为格式已经提供。问题是我需要两个源文件,一个包含问题+解决方案,另一个仅包含问题。
我希望只有一个.tex
包含问题和解决方案的文件,并且能够从中提取另一个.tex
不包含解决方案的文件。作为一个可能的框架,假设主.tex
文件如下:
% 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}
我想从中轻松获得一个具有空解决方案的类似解决方案:
% This is student.tex file
\documentclass{article}
\begin{document}
\begin{exercise}
This is the first exercise
\begin{solution}
\end{solution}
\end{exercise}
\end{document}
我想我正在寻找类似.dtx
文件的东西,但我从未使用过它,也许有更好的解决方案。我正在使用 Windows,因此grep
命令不起作用。
答案1
更新。
增加了支持在副本中保留空环境,仅删除其内容,或者也删除该\begin...\end
对(默认)。
我编写了一个 LuaLaTeX 解决方案,并尝试使其足够灵活。这些是组成该解决方案的文件:
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}
这个 MWE 定义了一个无操作solution
环境,它仅充当标记,但当然你可以以一种在 pdf 中产生一些效果的方式定义它。宏\omitEnvironment
指定你想要省略的环境。你可以多次使用此宏来指定多个环境,所有这些环境都将被省略。宏\omitFileSuffix
指定将附加到输出文件名的后缀。
跑步:
$ lualatex MWE.tex
您将获得两个文件(当然还有所有常见的辅助文件):
MWE.pdf
将像平常一样生成,并且所有内容(包括省略的环境)都将存在。MWE-sans-sol.tex
MWE.tex
是一份所有solution
环境均已被删除的副本。
$ diff MWE.tex MWE-sans-sol.tex
11,13d10
< \begin{solution}
< 2. \lipsum[2]\hrulefill\par
< \end{solution}
如果您只想删除解决方案的内容但保留空环境,则只需\leaveEmptyEnvs
在某个点指定MWE.tex
。在这种情况下,diff 将显示:
$ diff MWE.tex MWE-sans-sol.tex
12d11
< 2. \lipsum[2]\hrulefill\par
附言:感谢 Scott H.建议我不要使用 luatex 回调,这是我的第一个(也是太复杂的)方法
答案2
仅适用于 TeX 的解决方案;它假定您没有任何其他环境,其名称以soluti
之外的字符串开头solution
。
准备以下extract.tex
文件:
\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
根据需要更改文件名。将此文件与主文件放在一起,然后使用pdftex
或进行编译pdflatex
(它们是相同的)。
使用示例文件,生成的文件将是
% This is main.tex file
\documentclass{article}
\begin{document}
\begin{exercise}
This is the first exercise
\begin{solution}
\end{solution}
\end{exercise}
\end{document}
基本上,我们逐行读取主文件(忽略类别代码)\readline
;如果行以
\begin{soluti
然后我们写出该行,后面跟着一个空行,并将条件设置为 false;如果该行以
\end{solution
然后再次将条件设置为真。如果条件为真,则写出当前行。
答案3
我也遇到了同样的问题。我最终使用了docstrip
。
我设置的系统产生:
(i)将 tex 和 pdf 文件分别提供给我(附有解决方案)和学生(不带解决方案:),
(二)针对学生分组的练习的几个版本,以及
(iii) 排版仅选定的主题,允许我将所有练习保存在同一个文件中。
以下是包含练习和解决方案的主文件的概要:
% 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}
这是.ins
文件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
最后,一个写得很糟糕的 makefile --- 其实只不过是一个伪装的 bash 脚本。(我猜,理想情况下,运行make
应该会自动生成.ins
文件...啊,总有一天...)
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'
答案4
最简单的选择可能是提炼包,mbork 在评论中提到过。但是,这种方法不允许你将解决方案环境嵌套在练习环境中。
\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}
提取文件后的代码如下:
\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}