我正在尝试编写一个类似于练习包的包,这样我就可以为我的学生准备问题集。我希望延迟输出问题和/或解决方案,直到文档中出现合适的位置。我有一个可以工作的东西,但它有点笨重。例如,这个工作得很好:
\usepackage{environ}
\NewEnviron{testb}{\global\expandafter\let\csname bar\endcsname\BODY}
我使用是\csname
因为名称是动态生成的。如果命令不是 ,则效果不太好\BODY
。例如,如果我执行\let\bar{\BODY}
,latex 就会出现动脉瘤(它\bar
仅与 { 绑定)。
还有另一种方法可以做到这一点,即:
\NewEnviron{testc}{\global\expandafter\edef\csname foo\endcsname{\BODY}}
这有点管用。下面的方法管用:
\begin{testc}hi\end{testc}
,但这会让 LaTeX 出现动脉瘤:\begin{testc}\bf hi\end{testc}
。(错误消息是\incomplete
)。我尝试自己调试它,但我被我看不懂的包弄得一团糟。如果你用\tiny
而不是来做\bf
,你会得到截然不同的 LaTeX 错误消息:! TeX capacity exceeded, sorry [input stack size=5000].
我怎样才能将数据储存起来以供以后使用,不仅仅是\BODY
,还包括和其他东西的一些复杂组合\BODY
,同时又能使其对开始和结束之间的内容具有稳健性?
编辑:好的,下面有多个解决方案
答案1
有了\unexpanded
您就可以免去后顾之忧\protected@xdef
。
\documentclass{article}
\usepackage{environ}
\NewEnviron{exercise}{%
\xdef\savedexercises{%
\unexpanded\expandafter{\savedexercises}%
\noexpand\begin{printedexercise}%
\unexpanded\expandafter{\BODY}%
\noexpand\end{printedexercise}%
}%
}
\newcommand{\printexercises}{%
\savedexercises
\gdef\savedexercises{}%
}
\newcommand{\savedexercises}{}
\newtheorem{printedexercise}{Exercise}
\begin{document}
Here we talk about addition and show that $1+1=2$.
\begin{exercise}
Compute $1+2$
\end{exercise}
Here we talk about integrals.
\begin{exercise}
Compute the following integrals:
\begin{itemize}
\item $\displaystyle\int_0^x e^{-t^2}\,dt$
\item $\displaystyle\int_1^x \frac{e^t}{t}\,dt$, for $t>0$.
\end{itemize}
\end{exercise}
Now we can print the exercises.
\printexercises
\end{document}
我\newtheorem
只是为了举例子而用。
xparse
2019-03-05 或之后发布的版本:
\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\NewDocumentEnvironment{exercise}{+b}
{
\tl_gput_right:Nn \g_loisel_exercises_tl
{
\begin{printedexercise}
#1
\end{printedexercise}
}
}{}
\NewDocumentCommand{\printexercises}{}
{
\tl_use:N \g_loisel_exercises_tl
\tl_gclear:N \g_loisel_exercises_tl
}
\tl_new:N \g_loisel_exercises_tl
\ExplSyntaxOff
\newtheorem{printedexercise}{Exercise}
\begin{document}
Here we talk about addition and show that $1+1=2$.
\begin{exercise}
Compute $1+2$
\end{exercise}
Here we talk about integrals.
\begin{exercise}
Compute the following integrals:
\begin{itemize}
\item $\displaystyle\int_0^x e^{-t^2}\,dt$
\item $\displaystyle\int_1^x \frac{e^t}{t}\,dt$, for $t>0$.
\end{itemize}
\end{exercise}
Now we can print the exercises.
\printexercises
\end{document}
答案2
如果您想要BODY
保存然后动态添加内容,最好使用两个宏:
\documentclass{article}
\usepackage{environ}
\NewEnviron{testb}{%
\global\expandafter\let\csname bar\endcsname\BODY
\expandafter\xdef\csname barplus\endcsname{%
\expandafter\noexpand\csname bar\endcsname
\noexpand\bf Hi
}%
}
\begin{document}
\begin{testb}
\bfseries
Hi
\end{testb}
\show\barplus
\end{document}
如果你想避免使用,\BODY
你可以使用xparse
\documentclass{article}
\usepackage{xparse}
\NewDocumentEnvironment{testb}{+b}{\expandafter\gdef\csname bar\endcsname{#1}}{}
\begin{document}
\begin{testb}
\bfseries
Hi
\end{testb}
\show\bar
\end{document}
答案3
触发扩展的一个技巧是使用\romannumeral
:
当\romannumeral
(La)TeX 确实将一串数字加上一个空格作为它要转换的数字时,可扩展标记就会得到扩展。
当最终聚集在一起的数字不是正数时,转换的结果 (La)TeX 将不会提供任何标记。
因此,只要确保最终不会找到正数,就可以很好地(ab?)用于\romannumeral
触发大量的扩展工作和翻转参数工作。\romannumeral
这是一个变体egreg 的回答它用\romannumeral
和\exchange
代替\xdef
和\unexpanded
。
\documentclass{article}
\usepackage{environ}
\newcommand\exchange[2]{#2#1}
\NewEnviron{exercise}{%
\expandafter\gdef\expandafter\savedexercises\expandafter{%
\romannumeral0\expandafter\exchange\expandafter{\BODY}{%
\exchange{ }{\expandafter}\savedexercises
\begin{printedexercise}%
}%
\end{printedexercise}%
}%
}
\newcommand{\printexercises}{%
\savedexercises
\gdef\savedexercises{}%
}
\newcommand{\savedexercises}{}
\newtheorem{printedexercise}{Exercise}
\begin{document}
Here we talk about addition and show that $1+1=2$.
\begin{exercise}
Compute $1+2$
\end{exercise}
Here we talk about integrals.
\begin{exercise}
Compute the following integrals:
\begin{itemize}
\item $\displaystyle\int_0^x e^{-t^2}\,dt$
\item $\displaystyle\int_1^x \frac{e^t}{t}\,dt$, for $t>0$.
\end{itemize}
\end{exercise}
Now we can print the exercises.
\printexercises
\end{document}
如果您希望将要定义的宏的名称包装到\csname
..中\endcsname
,即如果您希望使用\csname savedexercises\endcsname
而不是\savedexercises
,您可以利用 (La)TeX 确实会扩展可扩展标记这一事实,同时由于\csname
收集控制序列标记的名称并在此搜索匹配的\endcsname
:
\documentclass{article}
\usepackage{environ}
\newcommand\exchange[2]{#2#1}
\NewEnviron{exercise}{%
\expandafter\gdef\csname savedexercises\expandafter\endcsname\expandafter{%
\romannumeral0\expandafter\exchange\expandafter{\BODY}{%
\exchange{ }{\expandafter\expandafter\expandafter}\csname savedexercises\endcsname
\begin{printedexercise}%
}%
\end{printedexercise}%
}%
}
\newcommand{\printexercises}{%
\csname savedexercises\endcsname
\expandafter\gdef\csname savedexercises\endcsname{}%
}
\expandafter\newcommand\expandafter{\csname savedexercises\endcsname}{}
\newtheorem{printedexercise}{Exercise}
\begin{document}
Here we talk about addition and show that $1+1=2$.
\begin{exercise}
Compute $1+2$
\end{exercise}
Here we talk about integrals.
\begin{exercise}
Compute the following integrals:
\begin{itemize}
\item $\displaystyle\int_0^x e^{-t^2}\,dt$
\item $\displaystyle\int_1^x \frac{e^t}{t}\,dt$, for $t>0$.
\end{itemize}
\end{exercise}
Now we can print the exercises.
\printexercises
\end{document}
请注意,目前介绍的方法不能用于\printexercises
在任意位置进行练习。您只能在文档中与练习环境后面的位置相对应的位置进行练习。
也许一个环境确实在 verbatim-catcode-régime 下读取其内容,以便将其未扩展地写入 .aux 文件,其中从 .aux 文件在 verbatim-catcode-régime 下读回它,也用于定义将\scantokens
应用于的宏,因此某种重新实现\label
-\ref
机制或\tableofcontents
逐字记录内容的 - 机制可能使练习在整个文档中可打印。
实现这样的机制可能是一个不错的挑战。但在考虑这一点之前,需要有关预期用途和所需“用户界面”的准确信息,即您希望能够以何种方式指定哪些其他内容,等等……
答案4
另一种变体,使用filecontentsdef v1.4
,支持verbatim
内容(如果需要)。
\documentclass{article}
\usepackage{filecontentsdef,pgffor}
\setlength{\parindent}{0pt}
\pagestyle{empty}
\newtheorem{exercise}{Exercise}
% savedexercise
\newcounter{exeNr}
\newenvironment{savedexercise}
{\stepcounter{exeNr}%
\begingroup
\filecontentsdefmacro{exercise-\the\value{exeNr}}}%
{\endfilecontentsdefmacro\endgroup}
\newcommand{\printexercise}[1]{\filecontentsexec{exercise-\the\numexpr#1\relax}}
\newcommand{\printsrcexercise}[1]{\filecontentsprint{exercise-\the\numexpr#1\relax}}
\begin{document}
\section{Talk about math}
Here we talk about addition and show that $1+1=2$.
\begin{savedexercise}
Compute $1+2$
\end{savedexercise}
Here we talk about integrals.
\begin{savedexercise}
Compute the following integrals:
\begin{itemize}
\item $\displaystyle\int_0^x e^{-t^2}\,dt$
\item $\displaystyle\int_1^x \frac{e^t}{t}\,dt$, for $t>0$.
\end{itemize}
\end{savedexercise}
\section{The exercises}
Now we can print the exercises.
\foreach \i in {1,...,2} {
\begin{exercise}
\printexercise{\i}
\end{exercise}
}
\section{The src exercises}
Now we can print the src of exercises.
\foreach \i in {1,...,2} {
\printsrcexercise{\i}
}
\section{Back to first exercises}
Remember the first exercise
\setcounter{exercise}{0}
\begin{exercise}
\printexercise{1}
\end{exercise}
\end{document}