\newline 似乎无法识别宏输出

\newline 似乎无法识别宏输出

我正在尝试制作一个宏。它在大多数情况下都有效,只是它有一些奇怪的行为。如果我在某个文本中调用宏,然后插入一个\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}

我得到:

Image of Latex Output: Before (newline) 12345 Luggage Way (newline) Portland, OR, 97231 After

我希望地址打印完成后而不是之前。

有趣的是,当我删除该行上除宏和 \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}

enter image description here

答案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}

enter image description here

相关内容