On the 'PT' question, what is needed is the lower case tokens p
and t
with category code 12. As you say, \the<dimen>
returns the value of the followed by pt
, but these are 'other' tokens and not 'letters'. So something like
\def\rem@pt#1.#2pt{...
will not work, even with \lowercase
(which changes character codes but not category codes): TeX would keep looking for the characters 'pt' with category code 'other', would probably not find them and would raise an error.
On the second question, \x\endgroup
will only define \rem@pt
inside the group you then close. You could use \gdef
to get round this, but the \expandafter
route also works as the content of \x
is expanded outside of the group. Your example still compiles as the kernel has already globally defined \rem@pt
. Try giving it a different name in your example and observe the 'Undefined control sequence error' that occurs when you expand \x
before closing the group.
An alternative way to create the same macro but without using \lowercase
is
\edef\rem@pt{%
\def\noexpand\rem@pt##1.##2\string p\string t{%
##1\noexpand\ifnum##2>\noexpand\z@.##2\noexpand\fi
}
}
\rem@pt
This works by creating the 'string' pt
using the \string
primitive (with e-TeX, you could also use \detokenize
). The first definition for \rem@pt
sets up for the second definition, which is then ultimately the same as the LaTeX kernel method.
A LaTeX3 implementation was requested in comments. At present, we have an internal function \__dim_strip_pt:n
to do the job: this is very much in the same vein as the LaTeX2e code, but with dimension expression evaluation. The set up currently reads
\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 }
}
}
where \dim_use:N \__dim_eval:w #1 \__dim_eval_end:
is the same as \dim_eval:n
but avoids needing to force expansion twice, which would be done using
\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
}
If there is a broader need for this 'strip the pt
function then it can be moved from internal to general use. (It's intended to support driver-level code, which is written by the team and is therefore internal.)
(Further updated answer, with a look deep down the Lion's Mouth in the last section.)
Joseph gave an answer (“No!”) to your actual question; I'll try to get your (and my) head around the “temporarily”. Let's start from TeX-by-Topic, bottom of page 129:
The \noexpand
command is expandable, and its expansion is the following token. The meaning of that token is made temporarily equal to \relax
, so that it cannot be expanded further.
At first I dismissed this as a colourful description, but one can easily see that TeX-by-Topic is right: The following short plain TeX file
\def\foo{bar}
\expandafter\show\noexpand\foo
\bye
prints
> \foo=\relax.
<recently read> \notexpanded: \foo
to the terminal, so \foo
is indeed “made temporarily equal to \relax
”. What I find very interesting is the \notexpanded: \foo
; this \notexpanded
isn't mentioned in the TeXbook or in TeX-by-Topic, and I only found one occurrence in Knuth's torture test for TeX. (Another test is to put \tracingcommands=2 \noexpand\foo
into your TeX file; then you'll find a \relax
in your log file.)
In the TeXbook there's a description that I find easier chewable than the one from TeX-by-Topic (since it doesn't contain “temporarily”): On page 214 it says
\noexpand
⟨token⟩. The expansion is the token itself; but that token is interpreted as if its meaning were ‘\relax
’ if it is a control sequence that would ordinarily be expanded by TeX’s expansion rules.
So what does “temporarily” mean?
In practise, “temporarily equal to \relax
” means the following: When TeX expands \noexpand\foo
, the result is \foo
with the temporary meaning \relax
, and as soon as TeX continues expanding, \foo
gets back its original meaning. A typical use case of \noexpand
, from Tex-by-Topic (page 130):
\edef\one{\def\noexpand\two{\the\prevdepth}}
Inside the \edef
, TeX doesn't expand \two
but continues with the next token {
(which is also not expandable). The \noexpand
is needed since otherwise TeX would try to expand \two
. This would cause an error if \two
is undefined, and it would cause much more trouble if \two
has been defined before. (Before \def
one doesn't need a \noexpand
since \def
is not expandable.) If you now \show
the \one
, then you see that the real \two
, not a \relax
ed one is inside \one
:
> \one=macro:
->\def \two {-1000.0pt}.
What's happening behind the scenes?
Let me first say this in the picture of TeX's mouth and stomach. In general, TeX continues chewing on a token list in its mouth as long as the first token is expandable. When the first token is unexpandable, it's swallowed for further processing in the stomach. Now \noexpand
indeed wraps the token following it in some protective coating. However, this coating is chewable; figure that it's made of sugar. Thus, when TeX chews on such a coated token, the coating is removed (and the token is expandable again), but TeX gets a sensory flash and thinks “Wow, this tastes like \relax
, I want to swallow it.” And sure enough, if the now uncoated token is the first in TeX's mouth (think of “first” as “closest to the throat”), then it is swallowed.
Two examples (the 2nd one being rather academic):
You have, e.g.,
\edef\bar{\noexpand\foo\anothercs}
Then inside the \edef
, \noexpand\foo
expands to a \relax
ed \foo
. This is converted back to \foo
, immediately swallowed, and TeX continues with expanding the next token \anothercs
.
You expand \noexpand\foo
twice, e.g. with
\def\foo{bar}
\expandafter\expandafter\expandafter\show\noexpand\foo
\bye
Then on the terminal you get
> \foo=macro:
->bar.
Thus, expanding \noexpand\foo
once gives a \relax
ed \foo
(as seen above), and the expansion of the \relax
ed \foo
is again the original \foo
. And this is not swallowed since it's not the first token!
Disclaimer: The above I found out by observation; it might be that some details are not entirely correct.
Some final remarks
Your attempt at defining \noexpand
as macro fails for several reasons. One technical point is that \let#1\relax
is not a good idea if #1
isn't a control sequence. Another point is that it's not really helpful to do \let#1\relax #1
; this has the same effect as \relax
as far as I can tell. (And as pointed out in the other answers, \let
is not expandable.)
Best Answer
For normal LaTeX usage and few lengths in points or other fixed unit I would recommend the usage of dimension registers (LaTeX: length, TeX:
\dimen
). Their benefit is that they are faster, already terminated and can be prefixed with a factor. The drawback is that you need to allocate the register and must be careful not to use it on its own inside a text.For
pgfmath
the benefit of the scale factor disappears almost completely because it allows you to use use*
for multiplication.One benefit for macros is that they can hold length with
em
orex
unit which are font size specific. If you assign such values to a length register they are converted topt
at the moment of assignment and not when they are used.If you use mostly
pgfmath
expressions and need several lengths incl. font size specific ones, I would stick to macros. You will need to remember if you dealing with lengths registers or macros in several cases anyway, so it would be better to stick with one thing.\dimexpr
makes this also a little simpler if you need a dimension expression.With eTeX there is also the possibility to define pseudo-lengths using
\dimexpr
. You can e.g. say\def\mylength{\dimexpr 1em\relax}
and use it like a length register, i.e. you can use a factor in front of it and can (and must) use\the
to get the string representation. Note thatpgfmath
handles lengths register by detecting its type but can't handle\dimexpr
yet. So\mylength
will be expanded first and the\dimexpr
will break. To use them inpgfmath
expressions you need to convert it to a string using\the
first. This is one thing which stops you from using\pgfmathsetlength
as a drop-in replacement of\setlength
for existing code.