![使用 `\@ifnextchar` 的可变参数](https://linux22.com/image/367797/%E4%BD%BF%E7%94%A8%20%60%5C%40ifnextchar%60%20%E7%9A%84%E5%8F%AF%E5%8F%98%E5%8F%82%E6%95%B0.png)
为了保持一致的引用样式,定义了一个\secn
能够处理多个参数的 Latex 宏。如果我提供一个参数s1
,它应该返回Section~\ref{sec:lbl}
。如果我提供多个参数s1
,...,,sn
它应该返回Sections~\ref{sec:s1}, ..., \ref{sec:s(n-1)} and \ref{sec:sn}
。
Sections
下面的 Latex 宏几乎实现了这一点,只是在有多个参数的情况下它不会生成复数。
\documentclass{article}
\def\secn{\myref{Section}{Sections}{sec}}
\makeatletter
\newcommand{\myref}[4]{\@ifnextchar\bgroup{#2}{#1}~\myreflist{#3}{#4}}
\newcommand{\myreflist}[2]{%
\@ifnextchar\bgroup{\ref{#1:#2}\myreflisttail{#1}}{\ref{#1:#2}}}
\newcommand{\myreflisttail}[2]{\@ifnextchar\bgroup{,
\ref{#1:#2}\myreflisttail{#1}}{ and \ref{#1:#2}}}
\makeatother
\begin{document}
\section{test}
\secn{s1} \par
\secn{s1}{s2} \par
\secn{s1}{s2}{s3}
\section{test1}
\label{sec:s1}
\section{test2}
\label{sec:s2}
\section{test3}
\label{sec:s3}
\end{document}
\@ifnextchar
定义中的命令总是\myref
返回 false。为什么会这样?我如何才能获得复数形式?
提前致谢
答案1
我建议不要使用带有可变参数的命令,而是使用以逗号分隔的列表作为参数的命令。您还可以使用包cleveref
,已经提供了这样的命令:
\documentclass{article}
\usepackage[sort]{cleveref}
\begin{document}
\section{test}
\cref{sec:s1} \par
\cref{sec:s1,sec:s2} \par
\cref{sec:s1,sec:s2,sec:s3}
\section{test1}
\label{sec:s1}
\section{test2}
\label{sec:s2}
\section{test3}
\label{sec:s3}
\end{document}
如果没有选项,则使用sort
默认值sort&compress
,结果如下:
答案2
您总是遇到 else 子句的问题,是因为您写了\newcommand{\myref}[4]{\@ifnextchar\bgroup{#2}{#1}~\myreflist{#3}{#4}}
,即\@ifnextchar\bgroup{#2}{#1}
总是将 视为~
下一个字符。 if-next-char 表示它后面的下一个字符,而不是它所属的宏!
您需要确保它后面没有任何内容,因此我建议将剩余的代码移到子句中:
\newcommand{\myref}[4]{\@ifnextchar\bgroup{#2~\myreflist{#3}{#4}}{#1~\myreflist{#3}{#4}}}
答案3
我会避免使用“可变数量的参数”:用逗号分隔的列表更容易输入和管理。
xparse
以下是使用和的实现expl3
:
\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\NewDocumentCommand{\generalref}{mmmm}
{
\kasper_generalref:nnnn { #1 } { #2 } { #3 } { #4 }
}
\NewDocumentCommand{\secn}{m}
{
\generalref{Section}{Sections}{sec}{#1}
}
\seq_new:N \l_kasper_generalref_seq
\cs_new_protected:Nn \kasper_generalref:nnnn
{
% clear the sequence
\seq_clear:N \l_kasper_generalref_seq
% populate it
\clist_map_inline:nn { #4 }
{
\seq_put_right:Nn \l_kasper_generalref_seq { \ref{#3:##1} }
}
% use first argument if less than two items
\int_compare:nTF { \seq_count:N \l_kasper_generalref_seq < 2 } { #1 } { #2 }
% no break between label and first reference
\nobreakspace
% issue the references (here ~ means a normal space)
\seq_use:Nnnn \l_kasper_generalref_seq
{ ~and\nobreakspace } % between two
{ ,~ } % between more than two
{ ~and\nobreakspace } % between last two
}
\ExplSyntaxOff
\begin{document}
\section{test}
\secn{s1} \par
\secn{s1,s2} \par
\secn{s1,s2,s3}
\section{test1}
\label{sec:s1}
\section{test2}
\label{sec:s2}
\section{test3}
\label{sec:s3}
\end{document}
但是,使用cleveref
更简单(并且更加可定制)。输出是相同的。
\documentclass{article}
\usepackage{xparse}
\usepackage[nosort]{cleveref}
\ExplSyntaxOn
\NewDocumentCommand{\gcref}{mm}
{% #1 is the common prefix, #2 is the list of labels
\kasper_gcref:nn { #1 } { #2 }
}
\NewDocumentCommand{\secn}{m}
{
\gcref{sec}{#1}
}
\seq_new:N \l_kasper_gcref_seq
\cs_new_protected:Nn \kasper_gcref:nn
{
% clear the sequence
\seq_clear:N \l_kasper_gcref_seq
% populate it
\clist_map_inline:nn { #2 }
{
\seq_put_right:Nn \l_kasper_gcref_seq { #1:##1 }
}
\Cref {\seq_use:Nn \l_kasper_gcref_seq { , } }
}
\ExplSyntaxOff
\begin{document}
\section{test}
\secn{s1} \par
\secn{s1,s2} \par
\secn{s1,s2,s3}
\section{test1}
\label{sec:s1}
\section{test2}
\label{sec:s2}
\section{test3}
\label{sec:s3}
\end{document}