我正在尝试制作一个宏。它在大多数情况下都有效,只是它有一些奇怪的行为。如果我在某个文本中调用宏,然后插入一个\newline
,它会在输出宏文本之前创建一个换行符。我希望将宏文本插入到换行符之前。
因此,当我使用代码时:
\documentclass[a4paper,12pt,noindent]{article}
\usepackage{parskip}
\makeatletter
\newcommand{\address}[1]{%
\ifdefined\@address % Address is already defined
\if\noexpand\@empty#1 % Argument is empty
\@address\mbox{}
\else % Argument is non-empty
\def\@address{#1}
\fi
\else % Address is not defined
\if\noexpand\@empty#1 % Argument is empty
\def\@address{}
\else % Argument is non-empty
\def\@address{#1}
\fi
\fi
}
\makeatother
\address{John Smith\\
12345 Luggage Way\\
Portland, OR, 97231}
\begin{document}
Before \address \newline
After
\end{document}
我得到:
我希望地址打印完成后而不是之前。
有趣的是,当我删除该行上除宏和 \newline 之外的所有文本时,LaTeX 会抱怨没有行结束。不知道为什么会这样。
答案1
当你这样做
\address \newline
标记\newline
被当作 的参数\address
。由于\@address
已在前言中通过调用进行了定义,因此采用\address
中的真分支。\ifdefined\@address
现在\if\noexpand\@empty\newline
测试。\if
条件句会完成扩展,直到找到不可扩展的标记。因此,它会扩展\noexpand
,这使得\@empty
暂时等同于\relax
;然后\newline
扩展;其标准定义是\protect\newline•
(其中•
代表名称中的空格)。在这种情况下,\protect
相当于\relax
(在正常排版期间),所以我们有
\if\relax\relax\newline•
测试返回 true。因此\newline•
执行:它包含获取新行的实际代码;\@address\mbox{}
如下所示。
如果定义宏为具有参数,它将总是寻找一个:如果{
后面跟着一个,则是括号之间的文本,或者是下一个标记。
您应该使用\printaddress
宏而不是重载\address
。
还要注意,这根本\if\noexpand\@empty#1
不是 是否为空的测试#1
。事实上,如果碰巧#1
真的是空的(调用\address{}
),你的代码会将\@empty
(相当于\relax
)与空格标记(跟在 后面#1
)进行比较,因此返回错误的。如果#1
不为空,则只要 的第一个标记#1
在完全扩展后不是字符标记,则测试将返回 true。
一个可靠的空虚测试是
\if\relax\detokenize{#1}\relax
正如我所说,我不会让 超负荷\address
,但如果这是你想要的,你需要重新定义它不带参数。第一次调用将设置地址,后续调用将打印该地址。
\documentclass{article}
\usepackage{parskip}
\makeatletter
\newcommand{\address}[1]{%
% this is for the first call
\def\@address{#1}%
% subsequent calls will print the address
\let\address\print@address
}
\newcommand{\@address}{}% initialize
\newcommand\print@address{\@address\mbox{}}
\makeatother
\address{John Smith\\
12345 Luggage Way\\
Portland, OR, 97231}
\begin{document}
Before \address \newline
After
\end{document}
答案2
egreg 已经解释了为什么测试等会失败。
下面是一些代码xparse
,以及g
建立由 分隔的可选参数的类型{}
(实际上,这很奇怪!)。我更喜欢传统的o
类型参数:
\documentclass[a4paper,12pt]{article}
\usepackage{parskip}
\usepackage{xparse}
\makeatletter
\NewDocumentCommand{\addressweird}{+g}{%
\@ifundefined{@addressweird}{%
\IfValueTF{#1}{%
\def\@addressweird{#1}%
}{%
\def\@addressweird{}%
}%
}{%
\IfValueTF{#1}{%
\def\@addressweird{#1}%
}{%
\@addressweird\mbox{}%
}%
}%
}
\NewDocumentCommand{\address}{+o}{%
\@ifundefined{@address}{%
\IfValueTF{#1}{%
\def\@address{#1}%
}{%
\def\@address{}%
}%
}{%
\IfValueTF{#1}{%
\def\@address{#1}%
}{%
\@address\mbox{}%
}%
}%
}
\title{Hello}
\makeatother
\addressweird{John Smith\\
12345 Luggage Way\\
Portland, OR, 97231
}
\address[Gandalf The Gray\\
Moria\\
Middle of Middle Earth
]
\begin{document}
Before \address \newline
After
\address
Before \addressweird \newline After
\end{document}