8

Is there an easy way to define a command that takes a word (an initialism) and inserts a period after every character? That is, I would like to define a command \init{abc} that outputs "A.B.C." (making no assumption on the number of characters in the input argument).

The intended use is to be able to switch the output of \init{#1} to \uppercase{#1} or \textsc{#1} depending on the desired style, or to distinguish between initialism like C.P.U. (each letter pronounced separately) and NASA (pronounced as one word) like the New Yorker does.

4

You can split the input at each token and then deliver them separated by periods; with \initformat you can choose the formatting, the argument should be a one parameter macro such as \MakeUppercase or \textsc.

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn
\NewDocumentCommand{\init}{m}
 {
  \tanh_init:n { #1 }
 }
\NewDocumentCommand{\initformat}{m}
 {
  \cs_set_eq:NN \tanh_init_format:n #1
 }

\seq_new:N \l_tanh_init_seq

\cs_new_protected:Nn \tanh_init:n
 {
  \seq_set_split:Nnn \l_tanh_init_seq { } { #1 }
  \tanh_init_format:n { \seq_use:Nn \l_tanh_init_seq { . } .\@ }
 }
\ExplSyntaxOff

\initformat{\MakeUppercase} % initialize

\begin{document}

\init{abc}

\initformat{\textsc}

\init{abc}

\end{document}

enter image description here

If you want to remove the trailing period when the initialism is followed by a period, you can check for it and take a decision. In the example I used \xspaceskip to make the effect more visible.

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn
\NewDocumentCommand{\init}{m}
 {
  \tanh_init:n { #1 }
 }
\NewDocumentCommand{\initformat}{m}
 {
  \cs_set_eq:NN \tanh_init_format:n #1
 }

\seq_new:N \l_tanh_init_seq

\cs_new_protected:Nn \tanh_init:n
 {
  \seq_set_split:Nnn \l_tanh_init_seq { } { #1 }
  \tanh_init_format:n { \seq_use:Nn \l_tanh_init_seq { . } }
  \tanh_init_period:
 }
\cs_new_protected:Nn \tanh_init_period:
 {
  \peek_charcode:NTF . { \@ } { \tanh_init_format:n { . } \@ }
 }
\ExplSyntaxOff

\xspaceskip=20pt

\initformat{\MakeUppercase} % initialize

\begin{document}

\init{abc} whatever

\init{abc}. A period!

\initformat{\textsc}

\init{abc} whatever

\init{abc}. A period!

\end{document}

enter image description here

If you want to preserve kerning, you need to make the test about . beforehand:

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn
\NewDocumentCommand{\init}{m}
 {
  \tanh_init:n { #1 }
 }
\NewDocumentCommand{\initformat}{m}
 {
  \cs_set_eq:NN \tanh_init_format:n #1
 }

\seq_new:N \l_tanh_init_seq

\cs_new_protected:Nn \tanh_init:n
 {
  \seq_set_split:Nnn \l_tanh_init_seq { } { #1 }
  \tanh_init_period:
 }
\cs_new_protected:Nn \tanh_init_period:
 {
  \peek_charcode_remove:NTF .
   {
    \tanh_init_format:n { \seq_use:Nn \l_tanh_init_seq { . } . } \spacefactor\sfcode`.~ 
   }
   {
    \tanh_init_format:n { \seq_use:Nn \l_tanh_init_seq { . } . } \@
   }
 }
\ExplSyntaxOff

\xspaceskip=20pt

\initformat{\MakeUppercase} % initialize

\begin{document}

\init{abp} whatever

\init{abp}. A period!

\initformat{\textsc}

\init{abp} whatever

\init{abp}. A period!

\textsc{a.b.p.} A period!

\end{document}

enter image description here

| improve this answer | |
  • That's a really nice solution, but to me it's a little less readable than what jarauh suggested. Does it offer any advantages besides the ability to change the format (\initformat{}) anywhere in the document? – tanh Nov 27 '15 at 17:38
  • @tanh Apart from the clearer syntax \init{abc} instead of \init abc\relax{}, you mean? – egreg Nov 27 '15 at 18:01
  • That is clearer, but I got around that by wrapping the \init command in @jarauh's answer above like this: \newcommand{\myinit}[1]{\init#1\relax{}}. I tried adding functionality for handling the case of an initialism ending a sentence (manually It is based on G.N.U\@.) by replacing .\@ toward the end of your code with \@ifnextchar.{\@.}{.\@} but I then get "IFNEXTCHAR" as part of the printed output. (I realize it must be obvious why this does not work if you know xparse package, but I couldn't figure it out.) – tanh Nov 29 '15 at 3:19
  • 1
    @tanh I added the code for peeking at a following period. – egreg Nov 29 '15 at 10:46
  • 1
    @tanh I'm not sure I see any difference; what font are you using? – egreg Dec 8 '15 at 19:25
3

One way to do it:

\documentclass{article}

\begin{document}
\newcommand{\init}[1]{%
  \let\nextinit=\relax
  \if\relax\noexpand#1\relax\else#1.\let\nextinit=\init\fi\nextinit}

\init IBM\relax{} is an abbreviation, as is
\init GNU\relax.  What is \init SPECTRE{}?
\end{document}

\init is now defined as a function that takes a single argument. Thus, in \init IBM, first, only the I will be the argument to \init. The idea is that \init checks whether its argument equals \relax. If yes, it does nothing and stops. If not, then it prints the argument plus a ., and it replicates itself. Thus it continues, until it meets a \relax. It also stops at {} (even though that's not strictly speeking a \relax, but well).

You could also try to implement other stopping criteria, such as a special character, using \@ifnextchar, see Understanding \@ifnextchar.

| improve this answer | |
  • That's very concise and works really well. I wrapped your \init command an a new command \myinit: \newcommand{\myinit}[1]{\init #1\relax{}} That way, I can can call \myinit{gnu} and make it easier to redefine the output format later. Thank you! (Edit: repeatedly and accidentally pressing return.) – tanh Nov 27 '15 at 17:20
  • I guess that should be \newcommand{\myinit}[1]{\init#1\relax{}\@} where the added \@ ensures a non-end-of-sentence space, as far as I know. – tanh Nov 27 '15 at 17:34
  • 1
    However, the \@ will also add a space if you write it at the end of a sentence before the punctuation mark. – jarauh Nov 27 '15 at 18:49
  • True. I guess the right thing to do is to print a \@. if the argument equals . (indicating the abbreviation is at the end of a sentence, as in It is based on \init GNU., which should expand to It is based on G.N.U\@.), and if not do the above. I tried modifying your code to do that, but do not have the skills. – tanh Nov 29 '15 at 3:02

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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