LaTeX 在ltfssbas.dtx
宏中定义一个叫做\strip@pt
(在第 [246] 行) 的宏,它pt
从 返回的维度中剥离部分\the\dimension
。该定义如下所示,是一个最小工作示例:
\documentclass[11pt]{article}
\begin{document}
\parindent=0pt
\makeatletter
\begingroup
\catcode `P=12 % digits and punct. catcode
\catcode `T=12 % digits and punct. catcode
\lowercase{%
\def\x{\def\rem@pt##1.##2PT{##1\ifnum##2>\z@.##2\fi}}}
\expandafter\endgroup\x%
\def\strip@pt{\expandafter\rem@pt\the}
\newdimen\normallineskiplimit \normallineskiplimit=0.001pt
\texttt{Original dimen \the\normallineskiplimit}\\
\texttt{Stripped dimen \strip@pt\normallineskiplimit}\\
\makeatother
\end{document}
在宏中,catcode
字母 'PT' 的 被改为类别 12,这是数字和标点符号的类别代码。然后它将其更改为lowercase
以匹配pt
返回的\the\dimension
。我可以理解其意图是用PT
返回的来分隔参数\the\dimension
,但为什么小写字母不能单独工作?还有第二个问题(请注意\relax
一个问题的限制)。就在 之前,它\def\strip@pt
写道\expandafter\endgroup\x
。这也可以写成\x\endgroup
并保存\expandafter
。在我提供的最小值中,两种方式都可以。我遗漏了什么吗?
答案1
关于“PT”问题,需要的是小写标记p
和t
类别代码为 12。正如您所说,\the<dimen>
返回后跟的值pt
,但这些是“其他”标记,而不是“字母”。所以类似
\def\rem@pt#1.#2pt{...
不起作用,即使使用\lowercase
(改变字符代码但不改变类别代码):TeX 将继续寻找类别代码为“other”的字符“pt”,可能找不到它们并会引发错误。
关于第二个问题,\x\endgroup
只会\rem@pt
在您关闭的组内定义。您可以使用\gdef
来解决这个问题,但该\expandafter
路线也有效,因为 的内容\x
在组外展开。您的示例仍可编译,因为内核已经全局定义了\rem@pt
。尝试在您的示例中为其指定一个不同的名称,并观察在关闭组之前展开时发生的“未定义的控制序列错误” \x
。
另一种创建相同宏但不使用的方法\lowercase
是
\edef\rem@pt{%
\def\noexpand\rem@pt##1.##2\string p\string t{%
##1\noexpand\ifnum##2>\noexpand\z@.##2\noexpand\fi
}
}
\rem@pt
pt
这是通过使用原语创建“字符串”来实现的\string
(使用 e-TeX,您也可以使用\detokenize
)。 的第一个定义\rem@pt
设置了第二个定义,最终与 LaTeX 内核方法相同。
评论中要求使用 LaTeX3 实现。目前,我们有一个内部函数\__dim_strip_pt:n
可以完成这项工作:这与 LaTeX2e 代码非常相似,但具有维度表达式评估。设置当前显示为
\cs_new:Npn \__dim_strip_pt:n #1
{
\exp_after:wN
\__dim_strip_pt:w \dim_use:N \__dim_eval:w #1 \__dim_eval_end: \q_stop
}
\use:x
{
\cs_new:Npn \exp_not:N \__dim_strip_pt:w
##1 . ##2 \tl_to_str:n { pt } ##3 \exp_not:N \q_stop
{
##1
\exp_not:N \int_compare:nNnT {##2} > \c_zero
{ . ##2 }
}
}
与\dim_use:N \__dim_eval:w #1 \__dim_eval_end:
相同,\dim_eval:n
但避免了强制扩展两次,可以使用
\cs_new:Npn \__dim_strip_pt:n #1
{
\exp_after:wN \exp_after:wN \exp_after:wN
\__dim_strip_pt:w \dim_eval:n {#1} \q_stop
}
如果对此有更广泛的需求,‘剥离该pt
功能,然后它可以从内部移到一般用途。’(它旨在支持由团队编写的驱动程序级代码,因此是内部的。)
答案2
ConTeXt 中使用了另一种定义,它不使用这个\lowercase
技巧。以下代码取自syst-aux.mkiv
。
%D \macros
%D {withoutpt,PtToCm,
%D numberofpoints,dimensiontocount}
%D
%D We can convert point into centimeters with:
%D
%D \starttyping
%D \PtToCm{dimension}
%D \stoptyping
{\catcode`\.=\othercatcode
\catcode`\p=\othercatcode
\catcode`\t=\othercatcode
\gdef\WITHOUTPT#1pt{#1}}
\def\withoutpt#1%
{\expandafter\WITHOUTPT#1}
%D The capitals are needed because \type{p} and \type{t} have
%D \CATCODE~12, while macronames only permit tokens with the
%D \CATCODE~11. As a result we cannot use the \type{.group}
%D primitives. Those who want to know more about this kind of
%D manipulations, we advice to study the \TEX book in detail.
%D Because this macro does not do any assignment, we can use it
%D in the following way too.
\def\PtToCm#1%
{\withoutpt\the\dimexpr0.0351459804\dimexpr#1\relax\relax cm}
与 LaTeX 宏不同,此宏不会尝试截断小数点后的数字,而是让用户\the
在需要时添加。