summaryrefslogtreecommitdiff
path: root/info/challenges/aro-bend/answer.015
diff options
context:
space:
mode:
authorNorbert Preining <norbert@preining.info>2019-09-02 13:46:59 +0900
committerNorbert Preining <norbert@preining.info>2019-09-02 13:46:59 +0900
commite0c6872cf40896c7be36b11dcc744620f10adf1d (patch)
tree60335e10d2f4354b0674ec22d7b53f0f8abee672 /info/challenges/aro-bend/answer.015
Initial commit
Diffstat (limited to 'info/challenges/aro-bend/answer.015')
-rw-r--r--info/challenges/aro-bend/answer.015830
1 files changed, 830 insertions, 0 deletions
diff --git a/info/challenges/aro-bend/answer.015 b/info/challenges/aro-bend/answer.015
new file mode 100644
index 0000000000..811e34eedb
--- /dev/null
+++ b/info/challenges/aro-bend/answer.015
@@ -0,0 +1,830 @@
+[The four parts of this answer were originally posted separately, as
+indicated in the subject lines.]
+
+Date: 16 Dec 1993 16:34:45 -0500 (EST)
+From: Michael Downes <MJD@MATH.AMS.ORG>
+Subject: Around the Bend #15, answers
+To: info-tex@shsu.edu
+
+Exercise 15 asked for a function \trimspace to trim a trailing space
+from the replacement text of a macro, and a function \trimspaces to
+trim both a leading and a trailing space. At the time of posting the
+exercise I had no prepared solution; as luck would have it the problem
+was rife with latent complications (including some hard questions
+about limiting the domain of application), which propagated an
+unusually diverse crop of approaches among the submitted solutions,
+and which made the task of preparing a good summary extraordinarily
+difficult. Even after breaking down the `summary' into two or three
+pieces, to avoid a too formidably large monolith of a posting, I'll
+have to leave out some material that I would otherwise have included.
+
+I'd say Donald Arseneau deserves credit for the best analysis,
+including an accurate survey of brace-stripping problems. Nearly
+everyone, including myself, had missed a lurking flaw of that kind in
+the first submitted version of their solution. Another good idea of
+Donald's that caught my fancy was to use TeX's built-in scanning
+procedures for <optional space> to strip the leading space in
+\trimspaces. I managed to work that into my own best solution, much to
+my satisfaction.
+
+Peter Schmitt came up with perhaps the most aerodynamic solution, on
+his second go-round. A solution by Ian Collier differed notably from
+the others by using \meaning to look for a leading space. Another
+submission, from Gary McGary, contained some original syntactic ideas,
+and explored the more general problem of removing an arbitrary token
+pattern at the end of a token list.
+
+A careless, off-the-cuff remark of mine in the statement of Exercise
+15 that after removing a leading space, \trimspaces should call
+\trimspace to remove a trailing space, was probably a mistake. In most
+cases, at least, \trimspaces can be more elegantly written by letting
+the two different space-removal procedures share a few tokens at a
+lower level.
+
+From Donald's analysis:
+
+> When I first read the question, I thought `why isn't there an answer
+> with the question, because that one is easy?' As I started to type
+> my answer `cold', I realized that what I had used previously to
+> ignore leading spaces ("\def\something#1#2\weird{#1#2}") had the bad
+> side-effect of stripping braces if the parameter began with "{".
+
+I append below Peter Schmitt's solution, more or less as he wrote it.
+The commentary refers to earlier correspondence in a place or two but I
+believe there is sufficient context to make everything intelligible.
+Test #5 in the test suite traps the insidious brace-stripping problem
+that infested most of the solutions in their first incarnation.
+
+More on Exercise 15 to follow, some time in the next few days.
+
+Michael Downes, mjd@math.ams.org
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+>>Solution 1 (Peter Schmitt, a8131dal@awiuni11.edvz.univie.ac.at)
+
+Since I wanted to stay with delimited arguments it was clear that one
+has to add a token (or tokens) in order to hide braces, which finally
+have to be removed again. First I came up with using \empty, as you
+did, but then I switched to a not expandable token because this can
+more efficiently be used as a parameter delimiter.
+
+\trimspaces and \trimspace are just used to expand the argument and
+add delimiting tokens in front and at the end of it, and set up the
+delimiting tokens for \Trimspace and \Trimspaces, too.
+
+As Donald does, I do not call \trimspace by \trimspaces but rather
+\Trimspace by \Trimspaces. It would be easy to offer \TrimLeft
+\TrimRight and \TrimBoth and also \TrimLeftS \TrimRightS and
+\TrimBothS which iterate in the (very unlikely!) case that there are
+several consecutive space tokens.
+
+\Trimspaces and \Trimspace remove leading, respectively trailing,
+spaces of the argument, but they both leave the delimiting tokens in
+place. These (and outside tokens) are removed by \TrimSpace in the
+process of redefining the initial controlsequence.
+
+\catcode`\<=3 \catcode`\>=3
+
+\def\trimspace #1{\expandafter\expandafter\expandafter
+ \Trimspace\expandafter <#1> >\\#1}
+\def\trimspaces #1{\expandafter\expandafter\expandafter
+ \Trimspaces\expandafter <#1>< <\\#1}
+
+%% \Trimspaces < text>< <\\ |< text>| ==>
+%% -> || + |text> + | <|
+%% => ||+| <|+|text>| == | <text>|
+%%
+%% \Trimspaces <text>< <\\ |<text>| ==>
+%% -> |<text>| + || + ||
+%% => |<text>|+||+|| == |<text>|
+
+%% \Trimspace <text > >\\ |<text >| ==>
+%% -> |<text| + | >|
+%% => |<text|+>\\ == |<text>\\|
+%%
+%% \Trimspace <text> >\\ |<text>| ==>
+%% -> |<text>| + ||
+%% => |<text>|+>\\ == |<text>>|
+
+\def\Trimspaces #1< #2<#3\\{\Trimspace #1#3#2 >\\}
+\def\Trimspace #1 >#2\\{\TrimSpace #1>\\}
+\def\TrimSpace #1>#2\\#3{%
+ \expandafter\expandafter\expandafter\expandafter\expandafter
+ \def \expandafter\expandafter\expandafter #3\expandafter
+ {\Remove#1}}
+ \def\Remove#1{}
+
+\catcode`\<12 \catcode`\>=12
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\def\Test#1{\def\test{#1}\immediate\write0{|\test|}%
+ \trimspaces\test
+ \immediate\write0{|\test|}%
+ }
+\let\trim\trimspace
+\let\trim\trimspaces
+
+%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\Test{}
+\Test{ }
+\Test{ a }
+\Test{ {}{} }
+\Test{{braces}}
+\Test{ {braces} }
+\Test{ { braces } }
+\Test{no space and no space}
+\Test{no space and a space: }
+\Test{ :a space and no space}
+\Test{ :a space and a space: }
+
+\def\test{ \ifx/ }\trimspace\test\show\test
+\def\test{ \ifx }\trimspaces\test\show\test
+
+\end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+Date: 23 Dec 1993 16:21:21 -0500 (EST)
+From: Michael Downes <MJD@MATH.AMS.ORG>
+Subject: Around the Bend #15, answers, 2nd installment
+To: info-tex@shsu.edu
+X-ListName: TeX-Related Network Discussion List <INFO-TeX@SHSU.edu>
+
+Some exposition seems called for here in order to lay out various
+considerations running through my mind and the minds of the other
+solution-submitters.
+
+TRIMMING A TRAILING SPACE
+
+There are two possible ways to remove a trailing space. The first one
+is to step through the given text one token at a time, and construct a
+new token list in parallel by adding the tokens one by one at the end.
+If the next token is a space, delay adding it until the subsequent
+token is checked, and if it turns out the text is exhausted, discard
+the space instead of adding it. The hard part about this approach is
+dealing with braces (character tokens with catcode 1 or 2) because a
+lone brace cannot be passed as a macro argument. A recent posting by
+\'Eamonn McManus to comp.text.tex on a different sort of problem
+showed that the braces can indeed be dealt with, it's just not easy.
+
+The second, simpler approach is to use TeX's scanning of delimited
+macro arguments to scan for the ending space and discard it. If you
+merely scan for a space token, however, you end up scanning through
+the given text `word' by `word' (word = sequence of non-space
+characters or brace-delimited groups) instead of token by token, which
+is perhaps if anything even more awkward than the first method above,
+since you still must deal with brace complications.
+
+The key refinement, therefore, is to scan for a pair of tokens: a
+space token and some well-chosen bizarre token that can't possibly
+occur in the scanned text. If you put the bizarre token at the end of
+the text, and if the text has a trailing space, then TeX's delimiter
+matching will match at that point and not before, because the earlier
+occurrences of space don't have the requisite other member of the pair.
+
+Next consider the possibility that the trailing space is absent: TeX
+will keep on scanning ahead for the pair <space><bizarre> until either
+it finds them or it decides to give up and signal a `Runaway
+argument?' error. So you must add a stop pair to catch the runaway
+argument possibility: a second instance of the bizarre token, preceded
+by a space. If TeX doesn't find a match at the first bizarre token, it
+will at the second one.
+
+Now all that's left is to test somehow where the hit occurred in order
+to fork properly. This can be done in various clever ways, as
+exhibited in the solutions.
+
+TRIMMING A LEADING SPACE
+
+More analysis from Donald Arseneau:
+
+> There are two safe, expandable ways to eat "one optional space":
+> "\ifnum" using an ascii code "`c" as the second number, and
+> "\ifdim" using a literal unit of measure like "pt". Oh, yes,
+> it could also be done with parameter syntax too, but more on
+> that later.
+
+In other words, one way to remove a leading space would be
+
+ \expandafter\def\expandafter\foo\expandafter{\ifdim0pt=0pt\foo \fi}
+
+The \expandafter's would cause the \ifdim to be executed first.
+Execution of the \ifdim will not terminate until the scanning of the
+second "0pt" is finished; therefore TeX will start expanding \foo as
+part of the scanning of the "0pt". Then if a space is the first thing
+inside the expansion of \foo, it will be removed by TeX as denoting
+the end of the dimension. Otherwise the first non-space token will
+terminate the dimension scanning and will be left in place (well, I am
+glossing over the problem of an expandable token at the beginning of
+\foo, which can be handled by further refinements).
+
+Notice that as written the trailing \fi will be included in the
+redefinition of \foo. No problem---just rewrite it with the \fi after
+the closing brace:
+
+ \expandafter\def\expandafter\foo\expandafter{\ifdim0pt=0pt\foo}\fi
+
+[Now for a sharp little question: will that work with \edef instead of
+\def?: \edef\foo{\ifdim0pt=0pt\foo}\fi. See if you can guess before
+testing it.]
+
+Other ways of removing a leading space include using \futurelet to
+look at the first token in the scanned text, or using TeX's argument
+delimiter scanning to scan for a space. The latter method is perhaps
+most straightforwardly done as a mirror-image of the method for
+removing a trailing space: make the delimiter <bizarre><space>, and
+then call the macro (let's say \trimx) by putting <bizarre> before the
+scanned text and a stop pair <bizarre><space> after it, in case a
+leading space is not present:
+
+ \trimx<bizarre>#1<bizarre> \endtrimx
+
+It would be possible to do without the bizarre token and have the
+delimiter consist only of a space, but with some ensuing
+complications, I think, that would make it scarcely worthwhile.
+
+SOME REMARKS ABOUT THE DOMAIN OF THE PROBLEM
+
+The application I had in mind was, generally speaking, to remove
+unwanted spaces at the beginning and end of a piece of text supplied
+by the user, such as a section title or other heading.
+
+Typical situation: A user command \title takes an argument
+
+ \title{ Some Article Title }
+
+with the definition of \title being
+
+ \def\title#1{\def\savedtitle{#1}\trimspaces\savedtitle}
+
+Thereafter we may use \savedtitle in any number of ways: print it; put
+it in a \mark for running heads; write it to an auxiliary file for
+table of contents use, or for adding to a BibTeX database; or write it
+on screen to show progress when typesetting a collection of articles.
+For the last two examples in particular trimming spaces with
+\ignorespaces or \unskip is undesirable.
+
+Notice also that \unskip will remove *any* trailing glue, including
+\leaders or explicit \hskip's that might sometimes be added by
+users for their own inscrutable purposes and whose unexpected
+removal could be (indeed, has been in true life) the cause of
+much consternation.
+
+If we call \trimspaces in the definition of \title, then leading and
+trailing spaces are removed once and for all, and none of the many
+functions that later use \savedtitle need to worry about that task.
+
+With this restricted domain of use in mind for \trimspaces, I screened
+the submitted solutions through the following conditions.
+
+Condition 1. The text has been stored in a macro. The result of
+\trimspaces is a redefinition of the macro.
+
+This is not exactly a necessary condition, but removal of this
+condition would suggest that constructions like
+
+ \def\foo#1{...
+ \message{Your argument "\trimspaces{#1}" makes me laugh}%
+ ...}
+
+should be supported. The full expansion done by \message or other such
+commands, however, can't be applied carelessly to arbitrary
+user-supplied text. You would need to deactive problematic elements
+(by changing catcodes, adding \protect's, whatever). So supporting
+full expansion for the operand of \trimspaces is of low relevance for
+the envisioned normal applications.
+
+Condition 2. It suffices to remove a single space before and after the
+text.
+
+In almost any other programming language, a typical space-trimming
+function would need to handle the possibility of multiple consecutive
+spaces. But in text supplied by an average user through the normal TeX
+lexical conventions, consecutive spaces will be reduced to a single
+space before our trimming functions are ever called.
+
+The next installment of this `summary' will include a recently arrived
+solution by Jonathan Fine that handles multiple trailing spaces as
+easily as a single one, without any extra implementation cost.
+
+Condition 3. For both the trailing space and the leading space, we
+don't know whether or not they are present.
+
+If we knew for certain that a given space was present, of course, the
+procedure for removing it would be easier.
+
+========================================================================
+>>Solution 2 (Ian Collier) [Ian.Collier@prg.oxford.ac.uk]
+
+... I used \meaning to find out whether or not the
+first character of the argument is a space (because spaces are usually
+ignored and this seems to be the only way to make the space visible).
+I'm fairly sure that "blank space" is the only \meaning beginning with
+"bl". I had rather a lot of trouble with braces, because if the first
+character is a brace then \meaning removes it and leaves an unmatched
+right brace. However I finally realised that \iffalse...\fi could be
+used to remove it.
+
+{\catcode`Q=3 \catcode`@=11
+ \gdef\trimspace#1{\expandafter\trimspac@a#1QAA QB}
+ \gdef\trimspac@x#1{\trimspac@a#1QAA QB}
+ \gdef\trimspac@a#1 Q#2{\if#2A#1\expandafter\trimspac@b
+ \else\trimspac@c#1\fi}
+ \gdef\trimspac@b A QB{}
+ \gdef\trimspac@c#1QAA{#1}
+
+ \gdef\trimspaces#1{\expandafter\expandafter\expandafter\tr@a
+ \expandafter\meaning#1A\fi{#1}}
+ \gdef\tr@a#1#2{\if#1b\if#2l\expandafter\expandafter\expandafter\tr@c
+ \else\expandafter\expandafter\expandafter\tr@b\fi\else
+ \expandafter\tr@b\fi}
+ \gdef\tr@b{\expandafter\trimspace\iffalse}
+ \gdef\tr@c{\expandafter\tr@d\iffalse}
+ \gdef\tr@d#1{\expandafter\tr@e#1Q}
+ \def\:{\gdef\tr@e}\: #1Q{\trimspac@x{#1}}
+}
+
+\def\test#1{\edef\text{#1}\immediate\write16 {"\trimspaces\text"}}
+\test{ Leading space}
+\test{Trailing space }
+\test{ Leading and trailing spaces }
+\test{Nospaces}
+\test{ {braces}Leading space{braces}}
+\test{{braces}Trailing space{braces} }
+\test{ {braces}Leading and trailing spaces{braces} }
+\test{{braces} Nospaces {braces}}
+\test{}
+\test{ }
+\test{\space\space{two spaces}\space\space}
+
+\end
+========================================================================
+
+Comments: Some extra work would be necessary to handle the possibility
+
+ \def\text{\iftrue a\else b\fi}
+ \trimspaces\text
+
+because removal of the \iftrue by \meaning will leave the \else and
+\fi unmatched, confusing the later \iffalse step done by \tr@b, \tr@c.
+But such a value for \text is rather unlikely in ordinary
+user-supplied arguments.
+
+Some more solutions to Exercise 15 will follow in a few days.
+
+Michael Downes %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+mjd@math.ams.org (Internet) ASCII 32--54,55--126: !"#$%&'()*+,-./0123456
+789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~
+
+Date: 30 Dec 1993 17:07:17 -0500 (EST)
+From: Michael Downes <MJD@MATH.AMS.ORG>
+Subject: Around the Bend #15, answers, 3rd installment
+To: info-tex@shsu.edu
+X-ListName: TeX-Related Network Discussion List <INFO-TeX@SHSU.edu>
+
+I have done some slight condensing in the answers, indicated by
+"[...]".
+
+Solution 3 by Greg McGary contains an interesting idea for an
+alternative syntax of the \trimspaces function: Instead of writing
+
+ \def\savedtitle{#1}\trimspaces\savedtitle
+
+you would write
+
+ \trimmed\def\savetitle{#1}
+
+========================================================================
+>>Solution 3 (Greg McGary, gkm@tmn.com)
+
+%%% preliminaries: (Mad about those abbreviations!)
+
+\catcode`@=11
+\let\ea=\expandafter
+\let\nx=\noexpand
+\let\ag=\aftergroup
+\def\agg{\ag\ag\ag}
+\let\bg=\begingroup
+\let\eg=\endgroup
+
+[...]
+
+%%% The underlaying tool I use is \trimmed, which is used as a modifier for
+%%% macro definitions to trim the trailing space from the body:
+%%% \trimmed\def\foo{foo } will set \foo to {foo}
+%%% Notice that any form of \def modifier may be interposed between \trimmed
+%%% and \def, as in \trimmed\global\long\outer\def\foo{foo }
+%%%
+%%% As an aside, TeX has no \expanded modifier. Expanded definitions
+%%% must be accomplished through use of \edef or \xdef (equivalent to
+%%% \global\edef) This is annoying, as we might like to use \trimmed with
+%%% expanded definitions and don't want to write a separate \etrimmed.
+%%% Luckily, we can easily roll our own \expanded modifier, like so:
+
+\def\expanded#1\def{#1\edef}
+
+%%% Other modifiers may optionally be inserted between \expanded and
+%%% \def, like so: \def\foo{foo} \outer\expanded\long\def\bar{\foo}
+
+%%% Here's the definition of \trimmed:
+
+\long\def\trimmed#1\def#2#3{\bg
+ \long\def\!##1##2 \!##3\trimmed@{\eg
+ \ifx\relax##3\relax
+ \trimmed@{##1}##2%
+ \else
+ ##1{##2}%
+ \fi}%
+ \!{#1\def#2}#3\! \!\trimmed@}
+
+\long\def\trimmed@#1#2\!{#1{#2}}
+
+%%% Notice the use of \begingroup...\endgroup to make the definition of \!
+%%% temporary so as not to disturb any previous definition, and so that the
+%%% temporary will disappear once we're done with it. Notice that the
+%%% \endgroup appears right away in the body of \!, so that the ensuing \def
+%%% will occur in the proper group. \! was chosen as a name for the temporary
+%%% macro because it is a non-alphabetic (non-catcode-11) character; any other
+%%% non-alphabetic would suffice as well. Non-alphabetic macro-names have the
+%%% desirable property of preserving any trailing space token.
+%%%
+%%% If we are really fastidious about keeping clutter out of the global name
+%%% space, we can also define \trimmed@ as a temporary alongside \!. We would
+%%% also want to use a name that's already defined, to avoid entering a new
+%%% name into TeX's hashtable. A non-alphabetic name like \: seems like a
+%%% good (though cryptic) choice:
+
+\long\def\trimmed#1\def#2#3{\bg
+ \long\def\:##1##2\!{\eg##1{##2}}
+ \long\def\!##1##2 \!##3\:{%
+ \ifx\relax##3\relax
+ \:{##1}##2%
+ \else
+ \eg##1{##2}%
+ \fi}%
+ \!{#1\def#2}#3\! \!\:}
+
+%%% Notice that we've had to delay the \endgroup until after our new
+%%% temporary \: has been used.
+%%%
+%%% Anyway, we may now define \trimspace as follows:
+
+\def\trimspace#1{\ea\trimmed\ea\def\ea#1\ea{#1}}
+
+%%% Notice that the replacement definition is a normal \def, whereas the
+%%% macro we started with could have had any number of modifiers attached,
+%%% such as \long, \outer, or \global. A further exercise might be to fix
+%%% this problem.
+%%%
+%%% A more generalized trim might allow any list of tokens to be trimmed off
+%%% the tail of another list of tokens. Here, we add an initial argument to
+%%% \trimmed specifying those tokens. In order to strip off trailing ".\par"
+%%% for instance, we could write: \trimmed{.\par}\outer\long\def\foo{foo.\par}
+%%%
+%%% Here's the general definition of \trimmed:
+
+\long\def\trimmed#1#2\def#3#4{\bg
+ \long\def\:##1##2\!{\eg##1{##2}}
+ \long\def\!##1##2#1\!##3\:{%
+ \ifx\relax##3\relax
+ \:{##1}##2%
+ \else
+ \eg##1{##2}%
+ \fi}%
+ \!{#2\def#3}#4\!#1\!\:}
+
+%%% The auxiliary \trimmed@ remains unchanged. Notice that we no longer really
+%%% need a non-alphabetic macro name for the temporary macro, since we don't
+%%% have to preserve the literal space token following the macro.
+%%%
+%%% Unfortunately, the literal space token problem doesn't disappear, it's just
+%%% pushed up a level. Now we have to give that space as an argument to \trimmed
+%%% in the definition of \trimspace, and hop over it with \expandafter!
+
+\edef\trimspace#1{\nx\ea\nx\trimmed\nx\ea
+ {\nx\ea\space\nx\ea}\nx\ea\def\nx\ea#1\nx\ea{#1}}
+
+%%% N.B., The curly braces, "\nx\ea{...\nx\ea}" around the "\nx\ea\space"
+%%% are necessary.
+%%%
+%%% This approach of defining \trimspace in terms of an underlaying \trimmed
+%%% \def'inition facility has the advantage of reusing code, but the
+%%% disadvantage of forcing a macro redefintion even if there is no trailing
+%%% space to remove. We could modify \trimmed to produce a new macro, \trim,
+%%% that redefines a macro only if it has the trailing pattern of interest.
+%%% (It also happens to be simpler!)
+
+\long\def\trim#1#2{\bg
+ \long\def\!##1#1\!##2\:{\eg
+ \ifx\relax##2\relax \else
+ \def#2{##1}%
+ \fi}%
+ \ea\!#2\!#1\!\:}
+
+%%% Now, we can define \trimspace in terms of \trim like so:
+
+\edef\trimspace#1{\nx\ea\nx\trim\nx\ea{\nx\ea\space\nx\ea}\nx\ea#1}
+
+%%% Ok, let's test it:
+
+\def\HasTrailingSpace{has trailing space }
+\def\NoTrailingSpace{no trailing space}
+
+\trimspace\HasTrailingSpace \show\HasTrailingSpace
+\trimspace\NoTrailingSpace \show\NoTrailingSpace
+
+%%% While we're at it, let's test another pattern:
+
+\def\HasTrailingDotPar{has trailing dot par.\par}
+\def\NoTrailingDotPar{no trailing dot par}
+
+\trim{.\par}\HasTrailingDotPar \show\HasTrailingDotPar
+\trim{.\par}\NoTrailingDotPar \show\NoTrailingDotPar
+
+%%% ### Exercise 15(b)
+%%% Write a macro \trimspaces that removes a leading space, if
+%%% present, and then calls \trimspace to remove a trailing space.
+
+%%% I'm going to solve this in a quick and dirty way, as it's getting
+%%% late and I'm running out of gas! Just use \futurelet sequestered
+%%% in a \vbox to inspect the first token. If it's a \space, gobble
+%%% the first token and subject the remaining tokens to \trimmed.
+
+\def\redefSansSp@ce#1 #2\redefSansSp@ce{\def#1{#2}}
+\def\redefSansSpace#1{\ea\redefSansSp@ce\ea#1#1\redefSansSp@ce}
+\def\trimspaces#1{\bg\setbox0=\vbox{%
+ \def\maybeRedefSansSpace{\ea\ifx\space\@\agg\redefSansSpace\agg#1\fi}%
+ \ea\futurelet\ea\@\ea\maybeRedefSansSpace#1}\eg
+ \trimspace#1}
+
+%%% \futurelet won't work for the more general case of trimming an
+%%% arbitrary leading pattern, as it only looks at one token.
+%%% I'll leave solving the general case as an exercise for the reader ;-)
+%%%
+%%% This is also not the most efficient solution, since we redefine the macro
+%%% twice if there is a leading space. Notice that we put the \setbox0
+%%% inside a group, to keep any previous definition of \box0 safe. This
+%%% is probably overkill, since \box0 is a temporary register and users
+%%% should be aware that it's fair game, but it doesn't hurt to be
+%%% courteous... Also note the abbreviation \agg, which pushes its argument
+%%% out two groups.
+
+[...]
+
+%%% Testing...
+
+\def\foo{ foo }
+\trimspaces\foo \show\foo
+
+========================================================================
+
+In the previous posting I discussed the method of removing a trailing
+space by scanning for a token pair <space><bizarre>. In Schmitt's
+solution, for example, the bizarre token was a greater-than character
+with catcode 3. And in my solution, I used a letter Q with catcode
+3. Solution 4 from Jonathan Fine takes the approach of using a second
+<space> token for the <bizarre> token. In practice this works for
+typical user-supplied text, as discussed before, since TeX's normal
+reduction of multiple spaces to single spaces makes the pair
+<space><space> sufficiently bizarre. I have to admit I like this idea;
+those who attempted a solution for this exercise and struggled with
+various other delimiter possibilities will, I think, appreciate the
+humor of it as I did.
+
+As I mentioned last week, I found some theoretical interest in the
+fact that if multiple space tokens were present at the end of the text
+being trimmed, Fine's solution would remove them all, without needing
+to use recursion. But another correspondent pointed out since then
+that if multiple spaces were present at the end they might also be
+presumed possible in the middle of the scanned text, and an occurrence
+of multiple spaces in the middle would cause \trim to fail.
+
+========================================================================
+>>Solution 4 (Jonathan Fine)
+
+%% NOTE: I have benefited from Michael Downes posting of answers, dated
+%% 16 December, particularly for stripping the leading space, and the
+%% discussion of the hazards of grouped arguments
+
+\catcode`\@=11
+%% The Solution
+\def\trim #1{\expandafter\trim@\expandafter{#1 }#1}
+\def\trim@ #1{\trim@@ @#1 @ #1 @ @@}
+\def\trim@@ #1@ #2@ #3@@{\trim@@@\empty #2 @}
+\def\unbrace#1{#1}
+\unbrace{\def\trim@@@ #1 } #2@#3{\expandafter\def
+ \expandafter #3\expandafter {#1}}
+
+%% Test Code
+\def\Test{\afterassignment\Test@ \def\test}
+\def\Test@{\trim\test \afterassignment\Test@@ \def\test@}
+\def\Test@@{\message{\ifx\test\test@ Y\else FAIL:|\meaning\test|\fi}}
+\catcode`\@=12
+
+%% Testing The Solution
+\Test{}{}
+\Test{ }{}
+\Test{ a }{a}
+\Test{ {}{} }{{}{}}
+\Test{{braces}}{{braces}}
+\Test{ {braces} }{{braces}}
+\Test{ { braces } }{{ braces }}
+\Test{no space and no space}{no space and no space}
+\Test{no space and a space: }{no space and a space:}
+\Test{ :a space and no space}{:a space and no space}
+\Test{ :a space and a space: }{:a space and a space:}
+\Test{ \ifx }{\ifx}
+\Test{ \ifx/ }{\ifx/}
+
+========================================================================
+
+Since my solution got rather long after I added some commentary I'll
+post it separately in a couple of days, rather than double the size of
+this post.
+
+Michael Downes %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+mjd@math.ams.org (Internet) ASCII 32--54,55--126: !"#$%&'()*+,-./0123456
+789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~
+
+Date: 03 Jan 1994 17:14:14 -0500 (EST)
+From: Michael Downes <MJD@MATH.AMS.ORG>
+Subject: Around the Bend #15, answers, 4th (last) installment
+To: info-tex@shsu.edu
+X-ListName: TeX-Related Network Discussion List <INFO-TeX@SHSU.edu>
+
+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:
+\toks0{{xyz}} \toks0{{xyz}}
+^^^^^^^---from \aftergroup ^^^^^^^---from \aftergroup
+\edef\x{\the\toks0} \edef\x{\the\toks0}
+
+========================================================================
+
+That's a wrap on Exercise 15.
+
+Michael Downes %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+mjd@math.ams.org (Internet) ASCII 32--54,55--126: !"#$%&'()*+,-./0123456
+789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~