这个问题是我的一个变体上一个问题请求一个以行的其余部分作为参数的宏。我想编写一个以下一个单词作为参数的宏,其中单词边界定义为空格或标点符号(可以是任何不是字母、数字或冒号字符的符号)。
困难在于\def
可以将常态设置为期望特定字符,而不是一组字符。
这不是一个理论上的挑战——具体的应用是一个可以做更好引用的宏,所以不用写Lemma~\cite{Lemma:Kantor}
,而是可以写\cf Lemma Kantor
近似值是
\def\cf #1 #2 {#1~\ref{#1:#2}}
但如果它在标点符号之前使用,它当然会失败。
答案1
您需要使用循环单独读取每个字符。实际上,您需要使用向前看,\futurelet
因为否则空格将被参数抓取器删除。
以下代码有效并将换行符视为空格。允许使用一个冒号作为两个单词之间的分隔符,而不是空格。如果要允许多个冒号,则必须将它们添加到列表if
中\cf@@
。
\documentclass{article}
\makeatletter
\def\cf{%
\begingroup
\def\name{}%
\cf@
}
\def\cf@{%
\futurelet\ntoken\cf@@
}
\def\cf@@{%
\ifcase 0%
\ifx\ntoken\@sptoken 0\else
\ifcat a\ntoken 1\else
\ifcat 0\ntoken 2\fi% test of token is catcode "other"
\fi\fi
\relax
\expandafter\cf@end
\or
\expandafter\cf@add
\else
\expandafter\cf@checknum
\fi
}
% Checks if token is a number (ASCII 48-57)
\def\cf@checknum#1{%
\ifcase 0%
\ifnum`#1>47
\ifnum`#1<58 1\fi\fi
\relax
\def\next{\cf@end#1}%
\else
\def\next{\cf@add{#1}}%
\fi
\next
}
\def\cf@add#1{%
\edef\name{\name#1}%
\cf@
}
\def\cf@end{%
\let\type\name
\edef\name{\name:}%
\def\cf@end{%
\edef\@tempa{%
\endgroup
\type\noexpand~%
\noexpand\ref{\name}%
}%
\@tempa
}%
% Remove colon
% support active colons (e.g. `[french]{babel}`)
\scantokens{\expandafter\let\csname cf@colon\endcsname=:}%
\@ifnextchar\cf@colon%
{\expandafter\cf@\@gobble}%
{\cf@}%
}
\makeatother
\begin{document}
\section{test}\label{sec:test}
\section{test}\label{sec:tes1}
\section{other}
\cf sec test it out
\cf sec:test it out
\cf sec
tes1 it out % numbers and line break work
\end{document}
答案2
以下是 Martin 代码的重写版本。首先是用例:
\documentclass{article}
\usepackage{cf}
\begin{document}
\section{Introduction}
\paragraph{Outline.}
In \cf Section things we shall discuss things, and then continue to discuss
stuff in \cf Section stuff; \cf Section final concludes.
\section{Things are so Important}\label{Section:things}
\section{Stuff is also Important}\label{Section:stuff}
\section{Conclusions}\label{Section:final}
\end{document
产生
现在,基本的样式文件是
\def\cf #1 {%
\def\kind@cf{#1}%
\beginIteration@cf
}
\def\beginIteration@cf{%
\begingroup
\def\idAccumulator@cf{}%
\iterateOnNextToken@cf
}
\def\iterateOnNextToken@cf{%
\futurelet\nextToken@cf
\examineNextToken@cf
}
\def\examineNextToken@cf{%
\ifcase 0% Trick: will expand the next tokens to see if more digits follow.
\ifx\nextToken@cf\@sptoken 0\else
\ifcat a\nextToken@cf 1\else
\ifcat 0\nextToken@cf 2\fi% test of token is catcode "other"
\fi\fi
\relax
\expandafter\endIteration@cf
\or
\expandafter\accumulateCharacter@cf
\else
\expandafter\cf@checknum
\fi
}
\def\accumulateCharacter@cf#1{%
\edef\idAccumulator@cf{\idAccumulator@cf#1}%
\iterateOnNextToken@cf
}
% Checks whether the parameter is a number (ASCII 48-57)
\def\cf@checknum#1{%
\ifcase 0%
\ifnum`#1>47
\ifnum`#1<58 1\fi\fi
\relax
\def\next{\endIteration@cf#1}%
\else
\def\next{\accumulateCharacter@cf{#1}}%
\fi
\next
}
\def\endIteration@cf{%
\edef\label@cf{\kind@cf:\idAccumulator@cf}%
\kind@cf~\expandafter\ref\expandafter{\label@cf}%
\endgroup
}