问题
稍后在 tex 文件中引用先前定义的环境内容的最佳方法是什么?具体来说,可以包含其他 latex 命令而不仅仅是文本的内容?
有关的:标记文本并在稍后引用(纯文本)
目标
为考试解决方案创建一个单独的页面。
执行
为了实现这一点,我创建了一个新环境sol
,该环境包装了docclasssolution
的原生环境exam
。然后,该包装器为解决方案主体内容创建了一个标签,我稍后会使用它来引用。
sol环境:
\makeatletter
\NewEnviron{sol}{%
\def\@currentlabel{\BODY}\label{solt:\thequestion}%
\begin{solution}%
\protect\BODY
\end{solution}%
}
\makeatother
然后我会定义我的问题及其解决方案:
\begin{questions}
\begin{question}
Test Question
\begin{sol}
Test Solution
\includegraphics[width=3cm,height=3cm,keepaspectratio]{image.png}
\end{sol}
\end{question}
\end{questions}
最后使用自定义命令打印解决方案,该命令将查看每个问题并找到其标记的解决方案:
\newcommand\printsolutions{%
\begin{multicols*}{2}
\xintFor* ##1 in {\xintSeq{1}{\thequestion}} \do {%
\def\x{##1}
\noindent
\x) ~\ref{solt:\x}\\
\\
}
\end{multicols*}
}
总共有四个文件:
- index.tex 包含配置和包含
- questions.tex 包含问题
- solutions.tex 包含解决方案
- index-solutions.tex 用于将解决方案编译成 pdf
索引.tex
\documentclass[addpoints,12pt]{exam}
\usepackage{amsmath}
\usepackage{graphicx}
\usepackage{array}
\usepackage{blindtext}
\usepackage{xintexpr}
\usepackage{pgffor}
\usepackage{environ}
\usepackage{multicol}
\makeatletter
\NewEnviron{sol}{%
\def\@currentlabel{\BODY}\label{solt:\thequestion}%
\begin{solution}%
\protect\BODY
\end{solution}%
}
\makeatother
\newcommand\printsolutions{%
\begin{multicols*}{2}
\xintFor* ##1 in {\xintSeq{1}{\thequestion}} \do {%
\def\x{##1}
\noindent
\x) ~\ref{solt:\x}\\
\\
}
\end{multicols*}
}
\begin{document}
\include{questions}
\include{solutions}
\end{document}
问题.tex
\begin{questions}
\begin{question}
Test Question
\begin{sol}
Test Solution
\protect\includegraphics[width=3cm,height=3cm,keepaspectratio]{image.png}
\end{sol}
\end{question}
\begin{questions}
解决方案.tex
\section*{\centering Solutions}
\printsolutions
索引-解决方案.tex
\includeonly{solutions}
\input{index}
问题
这种方法在 90% 的情况下都有效,但当 \BODY 参数包含诸如 \includegraphics 命令之类的元素时,这种方法就会失败。如果我保护 \includegraphics 命令,它就会完美地工作。我猜是因为它不会立即扩展 includegraphics 命令。
由于我无法始终知道 \BODY 的内容,因此我无法手动浏览并在麻烦的命令(例如 \includegraphics)上设置 \protect。有没有更好的方法可以解决这个问题?
答案1
您之所以\protect
提供帮助,是因为\label
使用\protected@write
将“受保护的扩展”写入\@currentlabel
文件.aux
。但是,正如您所想的那样,使用这种方法需要保护很多东西;因此,这种方法不太实用。
将解决方案存储在宏中可能是一种成功的方法,但它需要一个棘手的方面:在您的输入中,sol
环境嵌入在中question
,它本身嵌入在中questions
,并且在“仅打印解决方案”用例中,您不想打印问题文本。所以,在这种情况下(\ifmyPrintEverything
在我的例子中是当为假时),我定义questions
环境,以便它将question
环境定义为忽略所有内容,直到找到\begin{sol}
。这必须使用递归来完成,因为据我所知,在定义宏时,不能在分隔参数后的标记中使用带有 catcode 1 或 2 的括号(换句话说:我不能简单地用作\begin{sol}
宏参数分隔符,但使用\begin
是可以的;只需要检查是否{sol}
跟随,如果不跟随,则递归)。
以下代码适用于与示例中相同的简单结构:只有一个sol
环境作为主体中的最后一个内容question
,并且肯定没有sol
环境,等等。如果您的环境没有以其环境结尾,subquestions
那么这有点愚蠢,并且不会起作用。无论如何,我希望这能有所帮助。question
sol
常见.tex:
\documentclass[addpoints,12pt]{exam}
\usepackage{graphicx}
\usepackage{expl3}
\usepackage{environ}
\usepackage{multicol}
\usepackage{etoolbox}
\ExplSyntaxOn
% Borrow \int_step_inline:nnn from expl3
\cs_new_eq:NN \intstepinline \int_step_inline:nnn
\ExplSyntaxOff
\newif\ifprintSolutionsmode % initially false
\newtoks\mytoks
\makeatletter
\NewEnviron{sol}{%
\mytoks=\expandafter{\BODY}%
\csxdef{my@sol@\number\value{question}}{\the\mytoks}%
\unless\ifprintSolutionsmode
\begin{solution}
\BODY
\end{solution}%
\fi
}
\makeatother
\newcommand\printsolutions{%
\begin{multicols*}{2}
\intstepinline{1}{\number\value{question}}{%
\noindent ##1) \csuse{my@sol@##1}\par\vspace{\baselineskip}
}%
\end{multicols*}
}
\begin{document}
\ifmyPrintEverything
\printSolutionsmodefalse
\else
\renewenvironment{questions}
{\newenvironment{question}{\refstepcounter{question}\@my@skip@question}{}}
{}
\long\def\@my@skip@question#1\begin#2{%
\ifstrequal{#2}{sol}{\begin{sol}}{\@my@skip@question}%
}%
\printSolutionsmodetrue
\fi
\input{questions}\newpage
\printsolutions
\end{document}
问题.tex:
\begin{questions}
\begin{question}
Test Question.
\begin{sol}
Test Solution.
\includegraphics[width=3cm,keepaspectratio]{example-image}
\end{sol}
\end{question}
\begin{question}
This is another question.
\begin{sol}
Solution of the second question. \textbf{Non-expandable stuff}
\def\zzz{is fine}\zzz.
\end{sol}
\end{question}
\end{questions}
启动解决方案.tex:
\newif\ifmyPrintEverything
\myPrintEverythingfalse
\input{common}
启动-all.tex:
\newif\ifmyPrintEverything
\myPrintEverythingtrue
\input{common}
编译start-all.tex
结果:
编译start-solutions.tex
结果: