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.

I am looking for a macro that would take a string (better to say -- a sequence of words) and output the last word at the fist place before remaning part of the sequence. I want to put this macro into the definition of an environment intended to typeset personal information for a person:

\newenvironment{person}[2]%
{% begin definition
    \section*{#2}\label{#1}
    \addcontentsline{toc}{section}{#2}
}{% end definition
\clearpage
}

So that instead of

\addcontentsline{toc}{section}{#2}

there should be

\addcontentsline{toc}{section}{\reverseit{#2}}

The \reverseit macro should produce Alfv\'{e}n Hannes from Hannes Alfv\'{e}n and Allen James van from James van Allen. It should also handle exceptional case when its argument consist of a single word (eg, Aristotle).

Is is possible to compose such a macro within framework of TeX or, say, using Lua or LaTeX3?

A receipt proposed as an answert to How can I reverse the order of letters/tokens? solves my problem only in the case of a sequence which consists of two words.

share|improve this question
3  
Are we to take it that the separator between 'words' here is a space? Do we have to worry about expansion at all? –  Joseph Wright Jan 4 '13 at 13:59
    
@Josepf Wright: the 'words' are separated by spaces, but I am not sure if Alfv\'{e}n should be expanded of not. –  Igor Kotelnikov Jan 4 '13 at 14:11
    
I suggest you use some special separator that separates the surname from the first name. Otherwise, you'll get into trouble with Knuth as you wouldn't want Don Ervin Knuth to result in Knuth Ervin Don. –  Marc van Dongen Jan 4 '13 at 14:19

2 Answers 2

enter image description here

\documentclass{article}

\protected\def\reverseit#1{\xreverseit{}#1 \relax}
\def\xreverseit#1#2 #3{%
\ifx\relax#3%
#2#1%
\expandafter\xthree
\fi
\xreverseit{#1 #2}#3}
\def\xthree#1#2#3{}
\begin{document}

\reverseit{Hannes Alfv\'{e}n}

\reverseit{}

\reverseit{one}

\reverseit{one two}

\reverseit{one two three}


\end{document}
share|improve this answer
    
Thank you. It works for me. Some explanation on how it works would be appreciated. –  Igor Kotelnikov Jan 4 '13 at 15:04
    
Unfortunately, wizards like D.C. don't teach math :) The LaTeX kernel has \@gobble (\@gobbleone), \@gobbletwo and \@gobblefour, but left out \@gobblethree (=\xthree). –  Ahmed Musa Jan 4 '13 at 16:19
    
xreverseit collects words seen so far in #1 and parses up to the next space as #2 if #3 is \relax that is the end marker inserted at the start so do the reversal outputting #2 (the last word` followed by #1 (the preceding words and spaces). Otherwise recursively call the macro, adding the current #2 into the the words collected so far first argument of \xreverseit. The \xthree just gobbles the recursive call to terminate the recursion. –  David Carlisle Jan 4 '13 at 16:30
2  
@AhmedMusa it was left out deliberately as we didn't need it and we couldn't afford to waste a csname on an unused macro (latex2e on emtex had less than 100 spare csnames for all user-defined macros and cross references as I recall) –  David Carlisle Jan 4 '13 at 16:32

LaTeX3 provides useful tools which hopefully are easier to follow than David's code (examples shamelessly taken from his solution).

\documentclass{article}
\usepackage{expl3, xparse}
\ExplSyntaxOn
\seq_new:N \l_input_seq % declare a list (seq) variable
\tl_new:N \l_last_word_tl % declare a "token list" variable
\NewDocumentCommand{\reverseit}{m}
  {
    \tl_if_blank:nF {#1} % If the input contains no words, do nothing.
      {
        % Split the argument into words (~ stands for a space here).
        \seq_set_split:Nnn \l_input_seq { ~ } {#1}
        % Remove the last word from the seq.
        \seq_pop_right:NN \l_input_seq \l_last_word_tl
        % Put the last word on the left of the sequence.
        \seq_put_left:NV \l_input_seq \l_last_word_tl
        % Use the contents of the sequence, separating words by a space (~).
        \seq_use:Nnnn \l_input_seq { ~ } { ~ } { ~ }
      }
  }
\ExplSyntaxOff
\begin{document}

\reverseit{Hannes Alfv\'{e}n}

\reverseit{}

\reverseit{one}

\reverseit{one two}

\reverseit{one two three}

\end{document}
share|improve this answer
4  
"easier to follow" than my code, how could that be possible? (+1 anyway:-) –  David Carlisle Jan 4 '13 at 16:52
    
May you add the option of placing a comma after the first item (after exchange), if it's not the unique one? –  egreg Jan 4 '13 at 16:56
1  
@egreg Something like \seq_if_empty:NF \l_input_seq { \tl_put_right:Nn \l_last_word_tl { , } } between the pop and `put lines would do it, I think. –  Joseph Wright Jan 4 '13 at 17:05
    
@DavidCarlisle Well, more adaptable, perhaps. –  Bruno Le Floch Jan 7 '13 at 17:03
    
Joseph's approach should work. –  Bruno Le Floch Jan 7 '13 at 17:03

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.