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.

0

2 Answers 2

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

10
  • 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
    Commented Nov 27, 2015 at 17:38
  • @tanh Apart from the clearer syntax \init{abc} instead of \init abc\relax{}, you mean?
    – egreg
    Commented Nov 27, 2015 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
    Commented Nov 29, 2015 at 3:19
  • 1
    @tanh I added the code for peeking at a following period.
    – egreg
    Commented Nov 29, 2015 at 10:46
  • 1
    @tanh I'm not sure I see any difference; what font are you using?
    – egreg
    Commented Dec 8, 2015 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.

4
  • 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
    Commented Nov 27, 2015 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
    Commented Nov 27, 2015 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
    Commented Nov 27, 2015 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
    Commented Nov 29, 2015 at 3:02

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .