Take the 2-minute tour ×
TeX - LaTeX Stack Exchange is a question and answer site for users of TeX, LaTeX, ConTeXt, and related typesetting systems. It's 100% free, no registration required.

There's a couple of things I want to be able to do that are quite similar and basically involve keeping back a macro expansion until the last minute.

Possibly the simplest example is that I have a family of macros that expand out to variations of "object in X", but sometimes I override the default. So whilst \cobj expands to "object in C", \sobj expands to "set" and \gobj expands to "group". Where this causes trouble is when I want to talk about an object. Generically, I should type "an \cobj" but in the specific cases I may need to change to "a \sobj". The difficulty is that one reason for using these macros is so that I can change my mind as to which ones get special names and which don't. But it (partially) defeats the purpose if I have to go back through rewriting all the 'an's as 'a's. So what I'd like to do is define a macro '\a' which checks the next character for a vowel and produces 'an' or 'a' appropriately (modulo the odd special case!).

The difficulty with this is that it isn't followed by a character, it's followed by a macro. So I need to expand that macro to find out what character is the eventual first one. If it were a simple macro, say \def\cobj{object in C} then I could just do \def\a{\noexpand\@a} and \def\@a{\@ifnextchar{a}{an}{\@ifnextchar{e}{an}...}, but \cobj is quite a complicated macro and there's several levels to be expanded before one actually gets to the final characters. In particular, \cobj takes an optional argument.

So what I'd like is to be able to expand \a at the last possible minute. Sort of like \noexpand, but that only does a noexpand for one hop rather than 'end - 1' hops.

Anyone have any ideas as to how best to proceed? To give an indication of the level of answer that would be acceptable, I know about \expandafter and I'm not afraid to use it!


Edit: Here's another example that is a little more complicated. In a similar situation to the above, I have \dcat for "the category D". On a few rare occasions, I want to "eat" the "the", so I want a \nothe command that I can put beforehand: \nothe\dcat which eats the first word following it. It's simple enough to write a command that gobbles the next word in the input stream, but I want the next word in the output to disappear.

share|improve this question
    
Problem is to have the next command actually expand to arbitrary level. If we know that "the " will be coming up, we can do this with \protected\def\nothe{\expandafter\notheii\romannumeral0} \def\notheii the {} Here TeX will expand until it finds something telling it that no further digits follow (if necessary, swallowing a space). On the assumption that "he " will be produced at the same time as "t", we can then swallow the whole thing. So this will work even if \dcat is also a \protected\def. –  user9588 Jan 7 '12 at 14:06
add comment

6 Answers

up vote 15 down vote accepted

Another answer to your problem but not to your question, what about creating a "\newobject" command where you can specify whether it's an "a" or "an" (and then you're safe from any exceptions!) and provide you with a set of commands to typeset as you need. For example

\newobject{Gobj}{a}{group}
\newobject{Aobj}{an}{axiom}

\Gobj     % group
\aGobj    % a group
\theGobj  % the group

\Aobj     % axiom
\aAobj    % an axiom
\theAobj  % the axiom

You can of course define alternative interfaces, like \A\GObj or \The{gobj} or whatever looks better to you.

share|improve this answer
    
For some bizarre reason, this didn't occur to me! That the reason is bizarre is because I already have \dcatu for 'The category D' and '\dobjs' for 'objects in D'. I'd have to rethink my naming scheme, though, as it is quite possible for me to have '\aaobj' and '\Aaobj' already (don't ask unless you really want to know!). –  Loop Space Jul 27 '10 at 14:51
    
If you like the answer at least give it an up-vote ;) –  Juan A. Navarro Jul 27 '10 at 15:14
1  
Done! (It's not personal - I'm not a good voter, I have to admit! Possibly comes from being disenfranchised for the last decade. Still, at least then no-one can blame me for the governments that get voted in.) –  Loop Space Jul 27 '10 at 15:23
add comment

Nowadays there is little point in not using eTeX (all extended TeX variants like PDFTeX or LuaTeX or XeTeX include it), and macros defined with \protected\def and its variants expand only when being consumed or explicitly expanded with \expandafter. Inside of writes and edefs, they stay unchanged. Of course, that does not extend to any arguments of them since they are not considered arguments until the macro actually gets expanded.

share|improve this answer
add comment

When compiled, the file below produces two packages and then uses them in the following test file.

\documentclass{article}
\usepackage{nextword}
\ExplSyntaxOn
\newcommand { \nothe }
  { \nextword_do:n { \str_if_eq:xxF {\g_nextword_tl} {the} {\g_nextword_tl} } }
\ExplSyntaxOff
\begin{document}
\newcommand{\obj}[1][the default]{#1 object}
\nothe Hello, \unskip\nothe the World!
\nothe \obj [Any] is nice, including\nothe\obj s.
\end{document}

The first package provides tools to "expands" fully tokens, performing some TeX primitives along the way. This allows us to get close to TeX's output. On top of that is built the nextword package, which grabs the following word on output (currently the "word" stops at the next space, leaving it in the output). The word is saved in \g_nextword_tl. The command \nothe defined in the test file puts \g_nextword_tl back if it is not the.

\RequirePackage{filecontents}

\begin{filecontents}{sex.sty}
%% Package sex "Strong expansion for LaTeX"
\RequirePackage{expl3}
\ProvidesExplPackage{sex}{2011/07/12}{v<3}{Strong expansion for LaTeX}
\tl_new:N \g_sex_tl
\cs_new:Npn \sex:nf #1
  {
    \tl_gset:Nn \g_sex_tl {#1}
    \sex_aux:
  }
\cs_new:Npn \sex_aux:
  {
    \group_begin:
    \int_set:Nn \tex_escapechar:D { -1 }
    \group_align_safe_begin:
    \sex_peek:
  }
\cs_new:Npn \sex_peek: { \peek_after:Nw \sex_test: }
\cs_new:Npn \sex_test:
  {
    \token_if_expandable:NTF \l_peek_token
      { \exp_after:wN \sex_peek: }
      {
        \cs_if_exist:cTF
          { sex_ \token_to_meaning:N \l_peek_token : }
          {
            \group_align_safe_end:
            \exp_args:Nc \group_end:
              { sex_ \token_to_meaning:N \l_peek_token : }
          }
          {
            \token_if_group_begin:NTF \l_peek_token
              { \sex_bgroup: }
              {
                \token_if_group_end:NTF \l_peek_token
                  { \sex_egroup: }
                  {
                    \token_if_eq_meaning:NNTF \l_peek_token \c_space_token
                      { \sex_space: }
                      { \sex_stop: }
                  }
              }
          }
      }
  }
\cs_new:Npn \sex_stop:
  {
    \group_align_safe_end:
    \group_end:
    \g_sex_tl
  }
% Execute the following token (which may be {, ,}) before "\sex_aux".
\cs_new:Npn \sex_swap:
  {
    \group_align_safe_end:
    \group_end:
    \tex_afterassignment:D \sex_swap_aux:
    \cs_gset_eq:NN \g_sex_token
  }
\cs_new:Npn \sex_swap_aux: { \g_sex_token \sex_aux: }
\cs_new_eq:NN \sex_bgroup:     \sex_swap:
\cs_new_eq:NN \sex_egroup:     \sex_swap:
\cs_new_eq:NN \sex_space:      \sex_swap:
\cs_new_eq:NN \sex_begingroup: \sex_swap:
\cs_new_eq:NN \sex_endgroup:   \sex_swap:
\cs_new_eq:NN \sex_relax:      \sex_swap:
% Assignments: do the assignment, then regain control.
\cs_new:Npn \sex_assignment:
  { \tex_afterassignment:D \sex_aux: }
\cs_new_eq:NN \sex_let:         \sex_assignment:
\cs_new_eq:NN \sex_futurelet:   \sex_assignment:
\cs_new_eq:NN \sex_def:         \sex_assignment:
\cs_new_eq:NN \sex_edef:        \sex_assignment:
\cs_new_eq:NN \sex_gdef:        \sex_assignment:
\cs_new_eq:NN \sex_xdef:        \sex_assignment:
\cs_new_eq:NN \sex_long:        \sex_assignment:
\cs_new_eq:NN \sex_global:      \sex_assignment:
\cs_new_eq:NN \sex_protected:   \sex_assignment:
\cs_new_eq:NN \sex_escapechar:  \sex_assignment:
\cs_new_eq:NN \sex_endlinechar: \sex_assignment:
\cs_new_eq:NN \sex_catcode:     \sex_assignment:
% Document command
\cs_new_eq:NN \StrongExpand \sex:nf
\end{filecontents}


\begin{filecontents}{nextword.sty}
%% Package nextword "Catch the next word in the output."
\RequirePackage{sex}
\ProvidesExplPackage{nextword}{2011/07/12}{v0}{Next word of output}
\tl_new:N \g_nextword_tl
\tl_new:N \g_nextword_action_tl
\cs_new_protected:Npn \nextword_do:n #1
  {
    \tl_gset:Nn \g_nextword_action_tl {#1}
    \tl_gclear:N \g_nextword_tl
    \sex:nf
      {
        \cs_gset:Npn \sex_space:
          {
            \cs_gset_eq:NN \sex_space: \sex_swap:
            \tl_gset_eq:NN \g_sex_tl \g_nextword_action_tl
            \sex_stop:
          }
        \nextword_loop:N
      }
  }
\cs_new_protected:Npn \nextword_loop:N #1
  {
    \token_if_cs:NTF #1
      {
        \cs_gset_eq:NN \sex_space: \sex_swap:
        \g_nextword_action_tl
        #1
      }
      {
        \tl_put_right:Nx \g_nextword_tl
          {
            \exp_after:wN \nextword_meaning_to_char:w
            \token_to_meaning:N #1
          }
          \sex:nf { \nextword_loop:N }
      }
  }
\cs_new:Npn \nextword_meaning_to_char:w #1 ~ #2 ~ %
  {
    \prg_case_str:nnn {#1}
      {
        {math}       { \nextword_meaning_to_char_aux:w }
        {alignment}  { \nextword_meaning_to_char_aux:w }
        {macro}      { \nextword_meaning_to_char_aux:w }
      }
      { }
  }
\cs_new:Npn \nextword_meaning_to_char_aux:w #1 ~ { }
\end{filecontents}

% ===== Test file.
\documentclass{article}
\usepackage{nextword}
\ExplSyntaxOn
\newcommand { \nothe }
  { \nextword_do:n { \str_if_eq:xxF {\g_nextword_tl} {the} {\g_nextword_tl} } }
\ExplSyntaxOff
\begin{document}
\newcommand{\obj}[1][the default]{#1 object}
\nothe Hello, \unskip\nothe the World!
\nothe \obj [Any] is nice, including\nothe\obj s.
\end{document}
share|improve this answer
add comment

I'm accepting Juan's second answer, but I think I'll add a twist of my own (which Juan couldn't have suggested because he doesn't know about the internals of the macros that I'm defining!).

The macros that I'm defining already have variants that deal with capitals and plurals so it's no problem to add a new variant that correctly handles the article. The problem, as I indicated in my comment to Juan, is that the capitals and plurals are handled by suffixes but the article should be handled by prefixes and prefixes are already special.

In more detail, buried in the expansion of \dobj is something like '\my@lower{O}bject\my@singular{}{s} in D'. \dobj itself expands with \my@lower equivalent to \MakeLowercase and \my@singular just giving its first argument. \dobjs changes the behaviour of \my@singular, whilst \dobju changes that of \my@lower.

So I could easily throw in a new switch, say \my@article, which chose between "a" or "the" as appropriate and because it gets thrown in to the definition, I specify whether or not it is "a" or "an". My only problem is what to bind it to. In the above scheme, the obvious choice is \dobja, but that looks horrible! But I can't use \adobj because that conflicts with the fact that \dobj is really the simplest one of these that I have and the current record for most complicated is \KGvCGvTtobj [1]. So prefixes already have meaning and I don't want to overload them.

So what I'll try is to mix both my original plan and this solution. I'll write \a \dobj as I originally intended, but the \a won't eat the \dobj, rather it will set a flag telling the \dobj command to select the 'a/an' part of the \dobj command.

(except, of course, I can't use \a as that's already taken; boo-hoo)

[1] A monoid, with respect to the Tall-Wraith monoidal structure, in the category of co-V^-algebra objects in the category of complete V^ objects, where the '^*' indicates that everything is graded; just in case you were wondering.

share|improve this answer
add comment

It's quite hard to fully expand things which have a non-fixed number of arguments, at least unless you know enough to pick them up yourself. The reason is that as far as TeX is concerned your 'last possible moment' for expansion is when the token is read. For all TeX knows, your \a may alter the meaning of subsequent macros, category codes, etc.

Could you post a bit more detail on the nature of your optional arguments. It's possible to have a fully-expandable macro that takes optional arguments (see xparse for one approach), so you can imagine designing \a so that it looks for the optional argument, grabs it if appropriate, does an \edef for \cobj and finally does the stuff you want to do.

share|improve this answer
    
In all the examples that I can think of, I just have one optional argument for these commands (in the standard square brackets). So I think that this scheme would work. –  Loop Space Jul 27 '10 at 14:47
    
Ah, not so simple: I'm forgetting a key issue. You can only expandably check for an optional argument if there is a later mandatory one. I guess you could try LuaTeX, but over all juannavarroperez's suggestion looks the most robust approach to me. –  Joseph Wright Jul 27 '10 at 15:00
    
Really? Couldn't I just use \@ifnextchar? (incidentally, 'maters' -> 'matters' in your description on your user page) –  Loop Space Jul 27 '10 at 15:21
    
No. \@ifnextchar uses \futurelet, which is an assignment and is therefore not expandable. In xparse, the expandable method for finding an option argument has to absorb one token (or group) and do a comparison, hence the need to have a later mandatory argument. (P.S. Thanks for the proof-reading!) –  Joseph Wright Jul 27 '10 at 15:34
add comment

Maybe I'm missing something, but you could probably use \edef to expand the definition of \cobj (or whatever) and then find whether it starts with a vowel or not.

share|improve this answer
    
cobj can depend on a parameter, so I'd have to read in cobj and its parameters (if it has them), expand them, test them, and then insert them back into the stream. –  Loop Space Jul 27 '10 at 12:41
    
I've added another example which I think wouldn't work with what you said (or at least, where the dependence on the parameters is more obvious). –  Loop Space Jul 27 '10 at 13:11
    
Then you should probably enclose whatever you want to expand in braces and use \edef as I suggested. For example \a{\obj[C]}. –  Juan A. Navarro Jul 27 '10 at 14:35
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.