我正在检查我的一些旧宏,使它们完全可扩展。其中一个宏接受一个参数,该参数应该是两位数或四位整数(>1955)。然后,该宏将两位数转换为 1956-2055 范围内的四位年份,然后继续执行其他操作(基本上基于当前年份和给定年份之间的差异)。我想让它可扩展,因为我希望能够在例如中使用它。\href{mailto:foo\mymacro{98}[email protected]}{Mail me!}
现在,回顾代码,我可以看到我使用了很多作业,但是使用\numexpr
我可以摆脱所有这些,尽管当我分支不同的情况时需要多次进行相同的计算。
但我以前也做过一些健全性检查:如果#1
大于 99 但小于 1956,我会发出一个\PackageError
解释问题的代码。如果健全性检查成立,则直接跳过,因此其不可扩展性不是问题。此外,在普通文本中使用时,错误会按预期工作。然而,在 内部\href
,错误变为
Undefined control sequence.
\GenericError ...
#4 \errhelp \@err@ ...
这没有什么帮助。
有没有办法向用户提示问题所在?
附言:我不确定“可扩展上下文”是否适合描述这种情况。欢迎提出更好的标题。
编辑:例如,假设我想\mymacro
扩展给定年份和之间的差值的绝对值\year
。如果输入小于 100,我们应该将其解释为 1956-2055 范围内的年份。输入 100-1955 范围内的年份应该会出错。我现在得到的是
\def\mymacro#1{%
\ifnum\numexpr#1>1955 %
\ifnum\numexpr\year-#1\relax<0 %
\number\numexpr#1-\year\relax
\else
\number\numexpr\year-#1\relax
\fi
\else
\ifnum#1>99 %
ERROR!%
\else
\ifnum#1>56 % 19xx
\number\numexpr\year - 19#1\relax
\else % 20xx
\ifnum\numexpr\year - 20#1\relax<0
\number\numexpr20#1 - \year\relax
\else
\number\numexpr\year - 20#1\relax
\fi
\fi
\fi
\fi
}
%\def\test#1{#1: a\mymacro{#1}b}
\def\test#1{#1: \href{http://a\mymacro{#1}b}{Link}}
\test{96} \test{1996}; \test{08} \test{2008}; \test{11} \test{2011};
\test{20} \test{2020}
\test{123}
(a 和 b 只是用来检查空格是否悄悄出现)。所以我想用一些可以告诉用户问题的信息来替换 ERROR!,最好使用包含有问题的消息#1
。
答案1
我认为这是不可能的,因为的扩展\mymacro
应该始终可以被解释为\href
链接,但\PackageError
事实并非如此。
\href
您可以通过检查的 catcode 是否%
为 13 来测试您是否在的参数内部:
\ifnum\catcode`\%=\active\else\PackageError{...}{...}{...}\fi
但我不知道这段代码的健壮性如何。你能展示一下代码的相关部分吗?
答案2
我认为最好的方法是使用故意未定义但命名良好的控制序列。例如
\ifnum\SomeVar>100\relax
\expandafter\SignalError
\fi
...
\begingroup
\catcode`\!=11\relax
\catcode`\ =11\relax%
\gdef\SignalError{%
\ Input too big!%
}%
\endgroup%
使用 catcode 11 空间完成后,您将收到一个错误,该错误至少有一些描述,但不会在内部崩溃\edef
。您可能需要调整使用的字母数量以使其看起来“漂亮”,但这通常不太难。
答案3
与约瑟夫的回答类似,但允许任何错误消息(我很快会在一个包中使用它,我会尝试使它对错误输入非常稳健)。
\makeatletter
\catcode`\:=11\relax
\newcommand{\fp@error@aux}[1]
{\romannumeral \numexpr 0\@firstofone{\fp@error: #1}}
\newcommand{\fp@error}[1]
{\expandafter\fp@error@aux\csname fp info: #1\endcsname}
\fp@error{wrong input (123)}
给予
! Undefined control sequence.
<argument> \fp@error:
\fp info: wrong input (123)
这里,从和\csname fp info: #1\endcsname
之间的字符构建一个控制序列。TeX 不会让它保持未定义状态:相反,它会变成,这不会导致错误。相反,我们在附近放置了一个未定义的控制序列。改进了错误消息的格式,因为未定义的控制序列现在与 -ed 控制序列一起出现在 中。最后,我们需要一种干净的方法来摆脱,并且在这方面做得很好。\csname
\endcsname
\relax
\@firstofone
\fp@error:
\relax
<argument>
\relax
\romannumeral\numexpr0
编辑:在回答这个问题几个月后,我们将其添加\msg_expandable_error:n
到 LaTeX3。此命令接受一个(带括号的)参数并显示它(不展开它)。如果用户插入一些材料(响应I...
TeX 的提示),则在清理错误消息后插入该材料。
答案4
您可以定义\mymacro
当参数超出范围时扩展为空,并将其包装\href
在一个测试是否为空的宏中。
\documentclass{article}
\usepackage{hyperref}
\def\mymacro#1{%
\ifnum\numexpr#1>1955 %
\ifnum\numexpr\year-#1\relax<0 %
\number\numexpr#1-\year\relax
\else
\number\numexpr\year-#1\relax
\fi
\else
\ifnum#1>99 %
% out-of-range error!
\else
\ifnum#1>56 % 19xx
\number\numexpr\year - 19#1\relax
\else % 20xx
\ifnum\numexpr\year - 20#1\relax<0
\number\numexpr20#1 - \year\relax
\else
\number\numexpr\year - 20#1\relax
\fi
\fi
\fi
\fi
}
\begin{document}
%\def\test#1{#1: a\mymacro{#1}b}
\def\test#1{#1: \href{http://a\mymacro{#1}b}{Link}}
\test{96} \test{1996}; \test{08} \test{2008}; \test{11} \test{2011};
\test{20} \test{2020}
\def\hrefwrap#1{\def\tmpi{\mymacro{#1}}\if\relax\tmpi\relax
\typeout{error! (#1 out of range)}ERROR\footnote{#1 out of range}\else
\href{http://a\tmpi b}{Link}\fi}
\def\testi#1{#1: \hrefwrap{#1}}
\testi{96} \testi{1996}; \testi{08} \testi{2008}; \testi{11} \testi{2011};
\testi{20} \testi{2020}
\testi{123}
%\the\year
\end{document}