如何编写一个接受包含段落的参数的宏?

如何编写一个接受包含段落的参数的宏?

我正在尝试编写一个接受包含段落的参数的宏。如果您编写一个普通宏并且其参数之一包含一个段落,它将中断:

\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 的时候,处理一页文档需要几分钟,而且语法高亮还不存在,所以最好有某种机制来检测你是否忘记了}\def默认情况下, 不允许使用\par标记,除非你明确说明它是\long\def

\def\mymacro#1{#1}

另一方面,LaTeX 默认使用该命令,因此如果您使用正确的 LaTeX 命令(\def不应在 LaTeX 文档中使用),则默认\newcommand会生成\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的精确副本,而不依赖于。\par\oldpar\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不允许使用\partoken。它也不允许使用空行,因为\par在 TeX 将文本输入处理为 token 的阶段,空行会被转换为 token。请注意,\par在这方面重新定义是无用的,因为它恰恰不允许的标记\par,不管其含义如何。

解决方案:制作宏\long

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

更好的解决方案:

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

因为内部\newcommand使用。变体改为使用不带前缀的。\long\def\newcommand*\def

相关内容