两天来我一直在寻找解决方案,但我没有任何想法。这是我的问题。
我正在尝试创建一个宏来自动定义其他新宏,以便在文档中写入引用。我想创建\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 个表的小文档,然后尝试在文档中插入相应的参考资料。但是,结果并不如我所料,如下所示
我尝试了一些技巧,但没有成功:
\gdef
用。。。来代替\xdef
:- 但
\macro
编译时出现错误 - 但
\macro@i
问题仍然存在
- 但
- 使用(大量)
\expandafter
来强制扩展#1
,\macro@i
但我真的不知道在这种特殊情况下如何以及在何处使用它。
我知道有像varioref
和\labelformat
宏这样的包可以更好地完成这项工作。但是,由于是作为科学文章提交的,我需要一个不使用外部包的解决方案(而且我也非常好奇这个棘手问题的解决方案!)。
答案1
我的第一个赌注是
\newcommand\createrefmacro[1]{%
\expandafter\newcommand\csname ref#1\endcsname[2][]{#1~\ref{##2}}}
如果我加载xpatch
以查看\refFigure
after的真正含义
\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}