强制扩展宏参数,无需 \edef 或 \xdef

强制扩展宏参数,无需 \edef 或 \xdef

两天来我一直在寻找解决方案,但我没有任何想法。这是我的问题。

我正在尝试创建一个宏来自动定义其他新宏,以便在文档中写入引用。我想创建\createrefmacro一个接受一个参数的宏(例如Figure)。这\createrefmacro将创建一个名为的新宏\refFigure,它将接受一个参数(引用的标签)和一个可选参数(我需要一个可选参数)。

这是一个 MWE,它应该能重现我的问题(我的原始宏包含更多细节,而 MWE 可能看起来很愚蠢!)。

\documentclass{article}
  \makeatletter
  \def\createrefmacro#1{%
    \edef\macro{\csname ref#1\endcsname}%
    \edef\macro@i{\csname ref#1@i\endcsname}%
    \expandafter\gdef\macro{%
      \expandafter\@testopt\macro@i{}%
    }%
    \expandafter\gdef\macro@i[##1]##2{#1~\ref{##2}}%
  }
  \makeatother
  \createrefmacro{Figure}
  \createrefmacro{Table}
\begin{document}
  \begin{table}[htp]
    \caption{First table}\label{tab1}
  \end{table}
  \begin{figure}[htp]
    \caption{First figure}\label{fig1}
  \end{figure}
  \begin{figure}[htp]
    \caption{Second figure}\label{fig2}
  \end{figure}
  \refFigure{fig1}\par
  \refTable{tab1}\par
  \refFigure{fig2}\par
\end{document}

使用\createrefmacro{Figure}\createrefmacro{Table},我创建了\refFigure\refTable宏。我编写了一个只有 2 个图和 1 个表的小文档,然后尝试在文档中插入相应的参考资料。但是,结果并不如我所料,如下所示

在此处输入图片描述

我尝试了一些技巧,但没有成功:

  1. \gdef用。。。来代替\xdef
    • \macro编译时出现错误
    • \macro@i问题仍然存在
  2. 使用(大量)\expandafter来强制扩展#1\macro@i但我真的不知道在这种特殊情况下如何以及在何处使用它。

我知道有像varioref\labelformat宏这样的包可以更好地完成这项工作。但是,由于是作为科学文章提交的,我需要一个不使用外部包的解决方案(而且我也非常好奇这个棘手问题的解决方案!)。

答案1

我的第一个赌注是

\newcommand\createrefmacro[1]{%
  \expandafter\newcommand\csname ref#1\endcsname[2][]{#1~\ref{##2}}}

如果我加载xpatch以查看\refFigureafter的真正含义

\createrefmacro{Figure}

我得到以下答案:

> \\refFigure=\long macro:
[#1]#2->Figure~\ref {#2}.

这正是您希望通过您提出的(非工作)代码获得的结果。工作版本将是

\makeatletter
\def\createrefmacro#1{%
  \expandafter\def\csname ref#1\expandafter\endcsname\expandafter{%
    \expandafter\@testopt\csname ref#1@i\endcsname{}%
  }%
  \expandafter\def\csname ref#1@i\endcsname[##1]##2{#1~\ref{##2}}%
}
\makeatother

这样\createrefmacro首先会执行

 \expandafter\def\csname refFigure\expandafter\endcsname\expandafter{\expandafter\@testopt\csname refFigure@i\endcsname{}}

\expandafter第一对末尾的将\csname...\endcsname触发令牌的形成\refFigure@i,因此这将带给你

\def\refFigure{\@testopt\refFigure@i{}}

然后

\expandafter\def\csname refFigure@i\endcsname[#1]#2{Figure~\ref{#2}}

相当于

\def\refFigure@i[#1]#2{Figure~\ref{#2}}

但第一个解决方案显然更清洁。

分析

让我们看看你的代码在哪里失败了:

\def\createrefmacro#1{%
  \edef\macro{\csname ref#1\endcsname}%
  \edef\macro@i{\csname ref#1@i\endcsname}%
  \expandafter\gdef\macro{%
    \expandafter\@testopt\macro@i{}%
  }%
  \expandafter\gdef\macro@i[##1]##2{#1~\ref{##2}}%
}

首先,\gdef这是没用的,因为你可能不会\createrefmacro在组内发出,但它没有坏处。假设你给

\createrefmacro{Figure}

然后

\edef\macro{\csname refFigure\endcsname}

相当于

\def\macro{\refFigure}

(因为\refFigure仍然未定义,所以它将相当于\relax并停止扩展)。同样

\edef\macro@i{\csname refFigure@i\endcsname}

将与

\def\macro@i{\refFigure@i}

现在 TeX 将会看到

\expandafter\gdef\macro{\expandafter\@testopt\macro@i{}}

那将成为

\gdef\refFigure{\expandafter\@testopt\macro@i{}}

\expandafter主要问题在于:不是开始行动。所以,后面跟着\createrefmacro{Table}, 的意思\macro@i就是\refTable@i! 有一种可能性是\expandafter,让我们看看:

\expandafter\expandafter\expandafter\gdef\expandafter\macro\expandafter{%
  \expandafter\@testopt\macro@i{}}

你看到了吗?第一个\expandafter会扩展第三个,然后是第四个、第五个和第六个,剩下

\expandafter\gdef\macro{\@testopt\refFigure@i{}}

最终变成

\gdef\refFigure{\@testopt\refFigure@i{}}

按照期望。最后一项工作将是

\expandafter\gdef\macro@i[#1]#2{Figure~\ref{#2}}

(因为#1被替换为Figure并且##变成#),所以这将产生正确的

\gdef\refFigure@i[#1]#2{Figure~\ref{#2}}

当然,#1您的代码中没有解释应该做什么(但您确实这么说过)。但是,直接使用\csname...\endcsname更方便,不需要\macro\macro@i

答案2

我不太确定你想用默认参数做什么,但这似乎对你的 MWE 有用

\documentclass{article}

  \def\createrefmacro#1{%
    \expandafter\newcommand\csname ref#1\endcsname[2][default]{#1~\ref{##2}}}


  \createrefmacro{Figure}
  \createrefmacro{Table}

\begin{document}
  \begin{table}[htp]
    \caption{First table}\label{tab1}
  \end{table}
  \begin{figure}[htp]
    \caption{First figure}\label{fig1}
  \end{figure}
  \begin{figure}[htp]
    \caption{Second figure}\label{fig2}
  \end{figure}
  \refFigure{fig1}\par
  \refTable{tab1}\par
  \refFigure{fig2}\par
\end{document}

相关内容