Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Around the Bend #15, answers, 4th (last) installment

12 views
Skip to first unread message

Michael Downes

unread,
Jan 3, 1994, 5:14:14 PM1/3/94
to
My solution here is the result of weeks of incremental refinement,
ending only last week, and consequently benefits from analysis of the
other solutions.

========================================================================
>>Solution 5 (Michael Downes)

% Here I only solve part (b) of Exercise 15, in an attempt to make
% a solution of utmost compactness (3 control sequences, 45 tokens).
% Also, it seems likely that in actual use \trimspaces can be
% applied without harm whenever \trimspace might be needed.
%
% The method for pausing after each test might be of ancillary
% interest to some readers; unlike the alternative of setting
% \pausing=1, the \test's aren't required to be on separate lines.

\catcode`\Q=3

% \trimspaces\x redefines \x to have the same replacement text sans
% leading and trailing space tokens.
%
\def\trimspaces#1{%
% Use grouping to emulate a multi-token afterassignment queue.
\begingroup
% Put `\toks 0 {' into the afterassignment queue.
\aftergroup\toks\aftergroup0\aftergroup{%
% Apply \trimb to the replacement text of #1, adding a leading
% \noexpand to prevent brace stripping and to serve another purpose
% later.
\expandafter\trimb\expandafter\noexpand#1Q Q}%
% Transfer the trimmed text back into #1.
\edef#1{\the\toks0}%
}

% \trimb removes a trailing space if present, then calls \trimc to
% clean up any leftover bizarre Qs, and trim a leading space. In
% order for \trimc to work properly we need to put back a Q first.
%
\def\trimb#1 Q{\trimc#1Q}

% Execute \vfuzz assignment to remove leading space; the \noexpand
% will now prevent unwanted expansion of a macro or other expandable
% token at the beginning of the trimmed text. The \endgroup will feed
% in the \aftergroup tokens after the \vfuzz assignment is completed.
%
\def\trimc#1Q#2{\afterassignment\endgroup \vfuzz\the\vfuzz#1}

\catcode`\Q=11

\def\test#1{\errhelp{#1}\message{[\the\errhelp]}%
\edef\x{\the\errhelp}%
\global\tracingcommands2\global\tracingmacros2\global\tracingonline0
\trimspaces\x
\global\tracingcommands0\global\tracingmacros0\global\tracingonline0
\errhelp\expandafter{\x}\message{-> [\the\errhelp]}%
\read16 to\PressReturnToContinue
}

\test{ x } \test{ xy z } \test{} \test{{}}
\test{{}{}} \test{ {x} } \test{ } \test{{ }}
\test{\AA} \test{\fi} \test{\space x\space}
\test{ #1 }

\end

------------------------------------------------------------------------
Commentary

Suppose we have a macro \x with replacement text " {xyz} ". The task of
\trimspaces is to construct a statement of the form

\def\x{{xyz}}

i.e., to redefine \x with the same replacement text except for removal
of a leading or trailing space. However, a similar statement

\toks0{{xyz}}\edef\x{\the\toks0}

is more robust if the replacement text might contain # tokens. For
example,

\def\x{\def\y##1{}}

works OK but after thus defining \x, the statements

\def\trimx#1{\expandafter\def\expandafter\x\expandafter{#1}}
\trimx\x

fail with an error message because the "#1" in the definition of \y is
misinterpreted as a parameter token for the redefinition of \x.

Although # tokens seem highly unlikely in average user-supplied text, I
aimed for a statement of the second, robuster kind, as if I were writing
\trimspaces for use in a major macro package with thousands of
prospective users.

The basic structure of \trimspaces is therefore: First remove a trailing
space, then remove a leading space, then put the remaining text into
\toks0, then transfer the text to \x with \edef.

For removing the trailing space, I apply a macro scan with delimiter
<space,10><Q,3>. Here the notation <c,n> means the character token
consisting of character code c with catcode n.

The leading space is removed by executing the assignment
\vfuzz=\the\vfuzz at the beginning of the operand text, in order to use
a side effect of the assignment: removal of a following space. (Credit
to Donald Arseneau for this good idea.) The main reason for using
"\the\vfuzz" instead of 0pt is that it's slightly shorter (one token),
although if we did not have the group structure to localize the `change'
to \vfuzz, then using "\the\vfuzz" would also be a good idea for the
sake of preserving the variable's previous value.

The statement \vfuzz=\vfuzz (sans \the), by the way, would not gobble a
following space: when TeX recognizes a suitable variable on the
right-hand side of an assignment, it copies the value directly into the
left-hand side and skips the scanning process entirely.

Here's a step-by-step breakdown of the operation of \trimspaces through
two possibilities, one where both a leading and a trailing space are
present, and one where neither are present.

------------------------------------------------------------------------
Case 1 (spaces present) Case 2 (no spaces to be removed)
------------------------------------------------------------------------
\def\x{ {xyz} } \trimspaces\x \def\x{{xyz}} \trimspaces\x

Step 1: Step 1:
\begingroup... Same as for Case 1.
\expandafter\trimb
\expandafter\noexpand\x Q Q}...

Step 2: || Step 2: ||
\trimb\noexpand {xyz} Q Q... \trimb\noexpand{xyz}Q Q...
^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^
Here the row of ^^^ indicates the In this case the first Q is taken
material that is taken as argument up as part of #1, which is passed
#1 of \trimb, and || indicates the to \trimc. The second Q added by
tokens that match the macro \trimb therefore falls after the
delimiter. #1 is now passed to leftover Q instead of before.
\trimc, with another Q token added;
the leftover <space>Q token pair
follows.

Step 3: | Step 3: |
\trimc\noexpand {xyz}Q Q... \trimc\noexpand{xyz}QQ...
^^^^^^^^^^^^^^^ ^^ ^^^^^^^^^^^^^^ ^
Here we have #1, delimiter token Q, The situation at the end of the
and #2. The space before the second trimmed text ends up being the same
Q is skipped by TeX because it's as in Case 1, except for the
looking for a nondelimited argument absence of a space between the Qs.
for #2.

Step 4: Step 4:
\afterassignment\endgroup \afterassignment\endgroup
\vfuzz\the\vfuzz\noexpand {xyz}}... \vfuzz\the\vfuzz\noexpand{xyz}}...
^
Here the ^ marks the leading space
that is to be removed.

Step 5: \endgroup{xyz}}... Step 5: \endgroup{xyz}}...

\endgroup is from \afterassignment.

Step 6: Step 6:
\global\toks1{{xyz}} \global\toks1{{xyz}}
^^^^^^^^^^^^^^---from \aftergroup ^^^^^^^^^^^^^^---from \aftergroup
\edef\x{\the\toks1} \edef\x{\the\toks1}

========================================================================

That's a wrap on Exercise 15.

Michael Downes %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
m...@math.ams.org (Internet) ASCII 32--54,55--126: !"#$%&'()*+,-./0123456
789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~

0 new messages