LaTeX:如何根据另一个命令的存在来定义命令(例如`\nameref`)?

LaTeX:如何根据另一个命令的存在来定义命令(例如`\nameref`)?

我想使用\namerefhyperref但我也希望能够在没有的情况下呈现我的文档hyperref

因此,我想定义一个包装命令nameref(而不是自己定义“虚拟” \nameref),具体取决于是否\nameref定义。

答案最好与任何定义的命令一起使用,而不仅仅是\nameref

从类似的问题中,我试图得出一些解决方案不工作

\newcommand{\checkAndDef}[1]{%
\ifcsname#1\endcsname%
\newcommand{\refn}[1]{\nameref{#1}}%
\else%
\newcommand{\refn}[1]{[#1]}% no \nameref available
\fi%
}
\checkAndDef{nameref}%

请注意,\checkAndDef看起来很丑陋,我在无法进行文字检查后添加了它\nameref。我想我需要比现在更详细地了解 TeX 和 LaTeX 才能解决这个问题。

第一个解决方案仿照 Werner 的提议(不起作用):

\newcommand{\namerefcheck}{%
\ifcsname{}nameref\endcsname%
\newcommand{\refn}[1]{\nameref{##1}}%
\else%
\newcommand{\refn}[1]{[\detokenize{##1}]}% no \nameref available
\fi%
}
\AtBeginDocument{\namerefcheck}

该“解决方案”定义\refn为好像未加载 hyperref,无论是否加载。

笔记:如果您{}nameref用替换\nameref,那么它就会起作用(在我看来)。

答案1

这里发生了几件事:

  1. 在另一命令中定义命令时,需要重复使用参数符号。因此,#1应该变成##1#2应该变成##2,等等。从这个意义上讲,以下定义\checkAndDef可能就是您想要的:

    \newcommand{\checkAndDef}[1]{%
      \ifcsname#1\endcsname%
        \newcommand{\refn}[1]{\nameref{##1}}%
      \else%
        \newcommand{\refn}[1]{[\detokenize{##1}]}% no \nameref available
      \fi%
    }
    

    我添加了一个\detokenize到“无可用”选项,因为标签默认\nameref可以包含一些奇怪的字符(如下划线)。_

  2. hyperref通常应该在所有包中最后加载,因为它与许多文档元素交互,而所有这些元素都可能被其他包含的包更改。因此,\checkAndDef可能需要 \begin{document}在某些情况下。这是 特别需要的\nameref

\documentclass{article}

\usepackage{hyperref}% Comment out or not

\newcommand{\checkAndDef}[1]{%
  \ifcsname#1\endcsname
    \newcommand{\refn}[1]{\nameref{##1}}%
  \else
    \newcommand{\refn}[1]{[\detokenize{##1}]}% no \nameref available
  \fi
}

\AtBeginDocument{\checkAndDef{nameref}}

\begin{document}

\section{A section}\label{sec:section}

\refn{sec:section}

\end{document}

上述代码的结果是

在此处输入图片描述

何时hyperref加载,以及

在此处输入图片描述

什么hyperref时候不是已加载。

答案2

我提议

\usepackage{xparse}

\ExplSyntaxOn
\NewDocumentCommand{\conditionaldef}{mmO{0}omm}
 {% #1 = command to define
  % #2 = command to test
  % #3 = number of args,
  % #4 = default optional
  % #5 = replacement text if #2 exists
  % #6 = replacement text if #2 doesn't exist
  \AtBeginDocument
   {
    \cs_if_exist:NTF #2
     {
      \IfNoValueTF{#4}
       {
        \newcommand{#1}[#3]{#5}
       }
       {
        \newcommand{#1}[#3][#4]{#5}
       }
     }
     {
      \IfNoValueTF{#4}
       {
        \newcommand{#1}[#3]{#6}
       }
       {
        \newcommand{#1}[#3][#4]{#6}
       }
     }
   }
 }
\ExplSyntaxOff

你称之为

\conditionaldef{\refn}{\nameref}[1]{\nameref{#1}}{[\detokenize{#1}]}

完整示例。

\documentclass{article}
\usepackage{xparse}
\usepackage{hyperref}

\ExplSyntaxOn
\NewDocumentCommand{\conditionaldef}{mmO{0}omm}
 {% #1 = command to define
  % #2 = command to test
  % #3 = number of args,
  % #4 = default optional
  % #5 = replacement text if #2 exists
  % #6 = replacement text if #2 doesn't exist
  \AtBeginDocument
   {
    \cs_if_exist:NTF #2
     {
      \IfNoValueTF{#4}{\newcommand{#1}[#3]{#5}}}{\newcommand{#1}[#3][#4]{#5}}
     }
     {
      \IfNoValueTF{#4}{\newcommand{#1}[#3]{#6}}{\newcommand{#1}[#3][#4]{#6}}
     }
   }
 }
\ExplSyntaxOff

\conditionaldef{\refn}{\nameref}[1]{\nameref{#1}}{[\detokenize{#1}]}

\begin{document}

\section{Test}\label{test}

\refn{test}

\end{document}

输出是否hyperref已加载

在此处输入图片描述

hyperref如果未加载则输出

在此处输入图片描述

笔记

本质上,您使用的语法与 相同,因此也允许使用带有单个可选参数的命令。调用 时\newcommand无需重复。#\conditionaldef

没有xparse

\documentclass{article}
%\usepackage{hyperref}

%%% syntax
%% \conditionalref{<marg>}[<oarg>][<oarg>]{<marg>}{<marg>}
\makeatletter
\newcommand{\conditionaldef}[2]{%
  \@ifnextchar[{\cond@def@opt{#1}{#2}}{\cond@def@noopt{#1}{#2}}%
}
\newcommand{\cond@def@noopt}[4]{%
  \AtBeginDocument{%
    \ifdefined#2%
      \newcommand{#1}{#3}%
    \else
      \newcommand{#1}{#4}%
    \fi
  }%
}
\def\cond@def@opt#1#2[#3]{%
  \@ifnextchar[{\cond@def@twoopt{#1}{#2}{#3}}{\cond@def@oneopt{#1}{#2}{#3}}%
}
\def\cond@def@oneopt#1#2#3#4#5{%
  \AtBeginDocument{%
    \ifdefined#2%
      \newcommand{#1}[#3]{#4}%
    \else
      \newcommand{#1}[#3]{#5}%
    \fi
  }%
}
\def\cond@def@twoopt#1#2#3[#4]#5#6{%
  \AtBeginDocument{%
    \ifdefined#2%
      \newcommand{#1}[#3][#4]{#5}%
    \else
      \newcommand{#1}[#3][#4]{#6}%
    \fi
  }%
}
\makeatother

\conditionaldef{\refn}{\nameref}[1]{\nameref{#1}}{[\detokenize{#1}]}

\begin{document}

\section{Test}\label{test}

\refn{test}

\end{document}

更简单的版本

当然,如果你只是想定义,上面的内容就是浪费时间命令:一个简单的

\AtBeginDocument{%
  \ifdefined\nameref
    \newcommand{\refn}[1]{\nameref{#1}}%
  \else
    \newcommand{\refn}[1]{[\detokenize{#1}]}%
  \fi
}

就足够了。上面的基础设施允许定义许多命令无需代码重复。

答案3

假设令牌\BizArReUnDEFiNeD始终未定义,您可以执行以下操作:

\documentclass{article}

\makeatletter
% \checkAndDef{<other command>}%
%             {<definition-command><new command><parameter text>}%
%             {<new command's definition-text in case other command is undefined>}%
%             {<new command's definition-text in case other command is defined>}
%
% <parameter text> can be omitted.
%
\newcommand\checkAndDef[4]{%
  \AtBeginDocument{%
    \ifx\BizArReUnDEFiNeD#1%
      \expandafter\@firstoftwo
    \else
      \expandafter\@secondoftwo
    \fi
    {#2{#3}}{#2{#4}}%
  }%
}%
\makeatother

\usepackage{hyperref}
%\csname @ifpackageloaded\endcsname{hyperref}{}{\usepackage{nameref}}

\checkAndDef{\nameref}{\newcommand*\refn[1]}{\detokenize{[#1]}}{\nameref{#1}}%

\begin{document}
\section{Test-Section}\label{test}%
Some text. Reference: \refn{test}
\end{document}

相关内容