
Я пытаюсь написать себе пакет, похожий на пакет упражнений, чтобы я мог подготовить наборы задач для своих студентов. Я хочу отложить вывод задач и/или решений до тех пор, пока не будет достигнуто подходящее место в документе. У меня есть что-то, что работает, но это немного неуклюже. Например, это работает довольно хорошо:
\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 вообще не выдаст ни одного токена.
Таким образом, можно с успехом (не?)использовать \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
для того, чтобы упражнения происходили в произвольных местах. Вы можете иметь упражнения, происходящие только в тех местах документа, которые в источнике соответствуют местам за exercise-environments.
Возможно, среда, которая считывает свое содержимое в режиме 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}