Я хочу переопределить frame
как frame + itemize
. Я до сих пор пробовал это -
\documentclass{beamer}
\newenvironment{myframe}{\begin{frame}\begin{itemize}}{\end{itemize}\end{frame}}
\begin{document}
\begin{myframe}
\item
\end{myframe}
\end{document}
Что именно здесь не так и как это правильно переопределить?
решение1
Среда собирает свое тело beamer
, frame
используя следующую технику: она считывает и сохраняет токены, следующие \begin{frame}
за поиском \end{frame}
, не расширяя их, за исключением одного случая (см. ниже), но она примет \end{frame}
как конец собираемого тела среды, только если она увидела то же количество , \begin{...}
что и \end{...}
во время этого процесса сбора (она не пытается сопоставить их). Это то, что \beamer@begin@stack
используется для в \beamer@collect@@body
from beamerbaseframe.sty
.
В случае, если стек пуст (т.е. он увидел столько же, \end{...}
сколько \begin{...}
после \begin{frame}
), а следующее \end{...}
не является \end{frame}
, он расширяет это \end
в надежде, что это заставит \end{frame}
появиться. 1 Но этотолькопроисходит при условии, которое я указал ( должно быть замечено столько \end{...}
, сколько \begin{...}
после инициала ).\begin{frame}
В вашем примере \begin{itemize}
помещает один b
(т. е. открывает один уровень) в \beamer@begin@stack
стек. Это b
выталкивается при \end{myframe}
чтении. В этот момент \beamer@collect@@body
знает, что видел столько же, \begin{...}
сколько \end{...}
после \begin{frame}
. Это верно, но вводит в заблуждение, так как если бы \end{myframe}
были развернуты, это дало бы два выталкивания. Поэтому \beamer@begin@stack
думает, что следующий \end{...}
должен предоставить \end{frame}
, возможно, после расширения \end...
токена (см. сноску 1). Увы, это неверно; следующим \end{...}
является \end{document}
. \enddocument
таким образом расширяется, затем \beamer@collect@@body
продолжает искать, \end{frame}
но, конечно, никогда не находит его, достигает конца файла, что вызывает ошибку:
Runaway argument?
\let \AtEndDocument \@firstofone \@enddocumenthook \@checkend {docume\ETC.
! File ended while scanning use of \beamer@collect@@body.
<inserted text>
\par
Итак, по сути, проблема в том, что \beamer@collect@@body
невозможно вести правильный подсчет , \end{...}
потому что ваш \end{myframe}
скрывает два из них ( \end{itemize}\end{frame}
) и \beamer@collect@@body
не обнаружит их с помощью расширения , \endmyframe
несмотря на присутствие \end{myframe}
, потому что он увидел больше \begin{...}
, чем \end{...}
в точке, где он увидел это \end{myframe}
(непустой стек из-за \begin{itemize}
).
Решения требуют, чтобы вы не скрывали \end{...}
в макросах, которые \beamer@collect@@body
не будут расширяться. Один из них — использоватьenviron
пакет, как вответ ферахфезы, другой использует \itemize
и \enditemize
следующим образом (ни один из них не влияет на \beamer@begin@stack
стек, поэтому при \beamer@collect@@body
появлении \end{myframe}
стек пуст, поэтому \endmyframe
он расширяется один раз, что делает \end{frame}
процесс сканирования тела видимым для среды):
\documentclass{beamer}
\newenvironment*{myframe}[1]
{\begin{frame}{#1}%
\begingroup\itemize}
{\enditemize\endgroup
\end{frame}}
\begin{document}
\begin{myframe}{Frame title}
\item An item
\item Another item
\end{myframe}
\end{document}
Сноска
Точнее, происходит следующее (в
beamer 2018/12/02 v3.55
). Если:- стек
\beamer@begin@stack
пуст (столько, сколько\end{...}
было\begin{...}
увидено после того,\begin{frame}
чье тело собирается) и - следующее
\end{...}
не является\end{frame}
—скажем так, это\end{foobar}
,
затем
\beamer@collect@@body
заменяет расширение первого уровня\endfoobar\endgroup
на это\end{foobar}
в собранном материале. Это похоже на то, что\end{foobar}
произвело бы расширение, хотя последнее сделало бы несколько больше вещей (см.source2e.pdf
стр. 272):- проверьте, что заменяющий текст
\@currenvir
равенfoobar
(это приведет к сбою вbeamer
frame
процессе сбора тел, о котором мы говорим, поскольку\@currenvir
это такframe
); - соблюдать предыдущую
\@endparenv
команду (используется для подавления отступа абзаца в начале текста после некоторых сред создания абзацев, если только указанному тексту не предшествует пустая строка или\par
); - соблюдать предыдущую
\ignorespacesafterend
команду (используется для того, чтобы следующая команда\end{...}
игнорировала пробелы, которые могут следовать за ней).
- стек