如何可靠地存储文本片段以供日后使用

如何可靠地存储文本片段以供日后使用

我正在尝试编写一个类似于练习包的包,这样我就可以为我的学生准备问题集。我希望延迟输出问题和/或解决方案,直到文档中出现合适的位置。我有一个可以工作的东西,但它有点笨重。例如,这个工作得很好:

\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只是为了举例子而用。

在此处输入图片描述

xparse2019-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}

获得的输出示例

相关内容