如何寫一個接受包含段落的參數的巨集?

如何寫一個接受包含段落的參數的巨集?

我正在嘗試編寫一個接受包含段落的參數的巨集。如果您編寫一個普通的巨集並且其參數之一包含一個段落,它將中斷:

\documentclass{article}
\usepackage[utf8]{inputenc}

\title{test}

\begin{document}

\maketitle

\section{Introduction}

\def\mymacro#1{#1}

\mymacro{This

contains a paragraph}

\end{document}

這會產生一個錯誤:

Runaway argument?
{This
! Paragraph ended before \mymacro was complete.
<to be read again> 
                   \par
l.15

所以我嘗試重新定義\par以擴展巨集的參數,但現在它不會停止編譯:

\documentclass{article}
\usepackage[utf8]{inputenc}

\title{test}

\begin{document}

\maketitle

\section{Introduction}

\gdef\oldpar{\par}

\def\mymacro{\gdef\par{}\mymacroi}

\def\mymacroi#1{#1\gdef\par{\oldpar}}

\mymacro{foo

bar}

\end{document}

答案1

在編寫 TeX 時,處理一頁文件需要幾分鐘的時間,而且語法高亮也不是問題,因此如果有某種機制來檢測您是否忘記了}.預設情況下, A\def不允許使用\par令牌,除非您明確表示它是 a \long\def

\def\mymacro#1{#1}

另一方面,LaTeX 預設情況下使用它,因此如果您使用正確的 LaTeX 命令(\def不應在 LaTeX 文件中使用),則預設\newcommand會使用 a \long\def。如果您想要“短”,\def那麼您可以使用\newcommand*.

xparse傳回短參數預設值,但允許您\long使用+參數修飾符定義巨集:

\NewDocumentCommand\mymacro{ m}{#1}% \def
\NewDocumentCommand\mymacro{+m}{#1}% \long\def

你的第二次嘗試很聰明,除了兩件事之外它本來可以成功。

首先是您正在使用\gdef\oldpar{\par}然後\gdef\par{\oldpar}。一旦您擴張 \par你得到\oldpar哪個,當擴展時,產生\par哪個,當擴展時,產生\oldpar哪個,當擴展時,產生\par哪個,當擴展時,產生\oldpar......永遠運行:/

在這種情況下,您需要使用\let(或\global\let具有全域效果):\let\oldpar\par。這將建立\parname的精確副本\oldpar,它不依賴 is \par

其次,失控參數檢查是在較低層級實現的,與 的定義無關\par,因此這會失敗並出現相同的錯誤:

\let\par\relax
\def\mymacro#1{#1}
\mymacro{foo

bar}

因為當 TeX 看到兩個\endlinechar標記(預設是空格)時,TeX 會插入一個隱式\par標記,這會引發Runaway argument錯誤。知道了這一點,那麼:

\newcount\oldELchar
\oldELchar=\endlinechar
\def\mymacro{\endlinechar=-1\relax\mymacroi}
\def\mymacroi#1{#1\endlinechar=\oldELchar}
\mymacro{foo

bar}

不會引發錯誤,但新行將不再是空格。

答案2

用 定義的巨集的參數\def不允許\par使用標記。它也不允許空行,因為它們\par在 TeX 處理文字輸入到標記的階段被轉換成空白行。請注意,重新定義\par在這方面是沒有用的,因為它是恰恰不允許的標記\par,與其意義無關。

解決方案:製作你的巨集\long

\long\def\mymacro#1{#1}

更好的解決方案:

\newcommand{\mymacro}[1]{#1}

因為內部\newcommand使用。\long\def此變體\newcommand*使用\def不帶前綴的方式。

相關內容