我想重新定義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
,除非在一種情況下(見下文),但只有當它看到與此收集過程中的(它不會嘗試匹配它們)。這就是in from的用途。frame
\begin{frame}
\end{frame}
\end{frame}
\begin{...}
\end{...}
\beamer@begin@stack
\beamer@collect@@body
beamerbaseframe.sty
如果堆疊為空(即,它已經看到了\end{...}
與\begin{...}
after一樣多的值\begin{frame}
)並且後面的\end{...}
不是 an \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
and\enditemize
如下(它們都不影響\beamer@begin@stack
堆疊,因此當\beamer@collect@@body
see時\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
的第一級擴展來代替。這類似於擴展會產生的結果,儘管後者會做更多的事情(請參閱第 272 頁):\endfoobar\endgroup
\end{foobar}
\end{foobar}
source2e.pdf
\@currenvir
檢查is的替換文字foobar
(這在beamer
frame
我們正在討論的正文收集過程中會失敗,如\@currenvir
isframe
);- 遵循先前的
\@endparenv
命令(用於在某些段落製作環境之後抑製文字開頭的段落縮進,除非所述文字前面有空行或\par
); - 遵循上一個
\ignorespacesafterend
命令(用於使下一個命令\end{...}
忽略可能跟隨它的空格)。
- 堆疊