26

In LaTeX, how do I extract/isolate/determine the first and last characters of a macro argument?

Specifically, in the case I'm dealing with, the argument happens to be a base-10 integer (call it N). One way I can think of for the rightmost digit is to compute N mod 10, and for the leftmost digit to repeatedly divide by 10—except I haven't attempted anything like that yet and I don't know how reasonable that is or even if that is possible in (La)TeX. Extracting the characters as string entities would be fine too; I have no requirement of producing numeric values. (Edit: I forgot to mention earlier that I would prefer the solution to work for arbitrarily large numeric values—or at least up to, say, 7 or 10 digits—which might necessitate a string-base solution rather than a numeric solution.)

Ultimately, I just need the isolated characters in a form that I can compare using if/then constructs, e.g.:

\newcommand{\foo}[2]{%
  (something to extract rightmost digit of #1)
  (something to extract leftmost digit of #2)
  ...
  \ifthenelse{\rightmost\equal 2}{\kern.02em}{}%
  \ifthenelse{\rightmost\equal 4}{\kern.03em}{}%
  \ifthenelse{\rightmost\equal 7}{\kern-.02em}{}%
  ...
  \ifthenelse{\leftmost\equal 1}{\kern.02em}{}%
  \ifthenelse{\leftmost\equal 4}{\kern-.03em}{}%
  \ifthenelse{\leftmost\equal 5}{\kern.03em}{}%
  \ifthenelse{\leftmost\equal 7}{\kern.03em}{}%
  ...
}

Additional background: This is for fine-tuning the kerning in the numerator and denominator of fractions (related question Improving kerning in fractions).

3 Answers 3

24

You could use the xstring package

\documentclass{article}

\usepackage{xstring}

\newcommand{\mymacro}[1]{%
    \StrLeft{#1}{1}[\firstletter]%
    \StrRight{#1}{1}[\lastletter]%
    First letter: \firstletter

    Last letter: \lastletter
}

\begin{document}
    \mymacro{ABCDEF}
\end{document}

I’m a little busy so please excuse that I didn’t build it in you MWE ;-)

6
  • 1
    You should mention how to store the result in a variable. I know it is an optional argument somewhere, but I never remember where to place it. For the OP, that's going to be necessary to perform the tests afterwards. Feb 2, 2012 at 16:16
  • 1
    @BrunoLeFloch: Included it while you where writing, but thanks :-)
    – Tobi
    Feb 2, 2012 at 16:18
  • @Tobi: I discovered that the hard way, before you wrote this. Boy, was I surprised when \let\leftmost\StrLeft{#1}{1} didn't do what I expected! :) Feb 2, 2012 at 16:38
  • 1
    @ToddLehman: I’m not sure but I guess the problem with the \let is that it stores \StrLeft to \leftmost an ignores {#1}{1} so after that you could say \leftmost{#1}{1} but that doesn’t help ;-) After processing the \let operation the {#1}{1} is interpreted as more or less normal text and printed out
    – Tobi
    Feb 2, 2012 at 16:48
  • @Tobi: ooh—even better than \StrLeft and \StrRight are \IfBeginWith and \IfEndWith—also part of string—they don't require storing the value in a temporary variable. :) Feb 2, 2012 at 17:27
17

Or in classic TeX; the following defines \fst and \lst to be the first and last characters (or be empty for short input)

\def\fl#1{\flx#1\empty\empty\empty}
\def\flx#1#2#3\empty{%
\edef\fst{#1}%
 \edef\cdar{#2}%
 \edef\cddr{#3}%
  \ifx\cddr\empty
    \let\lst\cdar
  \else
     \expandafter\flxx
   \fi
  #3}
\def\flxx#1#2\empty{%
 \edef\car{#1}%
  \ifx\car\empty
  \else
     \let\lst\car
     \expandafter\flxx
   \fi
  #2\empty}

\immediate\write20{===}
\fl{}\immediate\write20{[\fst][\lst]}
\immediate\write20{===1}
\fl{1}\immediate\write20{[\fst][\lst]}
\immediate\write20{===12}
\fl{12}\immediate\write20{[\fst][\lst]}
\immediate\write20{===123}
\fl{123}\immediate\write20{[\fst][\lst]}
\immediate\write20{===1234}
\fl{1234}\immediate\write20{[\fst][\lst]}

\end



===
[][]
===1
[1][]
===12
[1][2]
===123
[1][3]
===1234
[1][4]
4
  • It is so cool that there are so many ways to do this! Feb 2, 2012 at 17:29
  • :-) It should be (car and (cdr which are, in lisp the head and tail respectively of a list, the names being IBM 704 assembler instructions, originally en.wikipedia.org/wiki/CAR_and_CDR Feb 2, 2012 at 19:32
  • as for local groups yes could be grouped, or you could avoid defining macros at all and just pass them to a following command as #1 and #2, it depends how the OP wants to use it... Feb 2, 2012 at 19:35
  • David: My comment, to which you have responded, was inadvertently deleted. Thanks for your response.
    – Ahmed Musa
    Feb 3, 2012 at 15:55
13

Here are 3 solutions using expl3, depending on your requirements. To perform the tests, I'd use \prg_case_int:nnn rather than an ad-hoc bunch of \ifthenelse statements: those are slower.

(1) The simplest is to use \tl_head:n{#1} to access the first digit of the number, and \int_mod:nn {#1}{10} for the last one. This works for numbers less than 2^{31}, and with versions of expl3 from roughly June 2011 onwards.

\documentclass{article}
\usepackage{expl3}
\ExplSyntaxOn
\newcommand {\foo} [2] {
  \prg_case_int:nnn { \int_mod:nn {#1} {10} }
    {
      {1} { \kern .02em \relax }
      {4} { \kern .03em \relax }
      {7} { \kern -.02em \relax }
    }
    { }
  \prg_case_int:nnn { \tl_head:n {#2} }
    {
      {1} { \kern .02em \relax }
      {4} { \kern -.03em \relax }
      {5} { \kern .03em \relax }
      {7} { \kern .03em \relax }
    }
    { }
}
\ExplSyntaxOff

(2) With a recent version of expl3 (mid-january 2012 onwards), using \tl_item:nn is probably best, as it will work for any integer, and you probably don't really want to enforce that your numerator and denominator are integers anyways. Namnely, \tl_item:nn {#1} {<integer>} gives the <integer>-th token in the token list, starting at zero for the left-most token (first digit), with negative indices starting at -1 for the right-most token. In particular,

\tl_item:nn { abcd } { 0 } % => a, left-most token
\tl_item:nn { abcd } { 2 } % => c
\tl_item:nn { abcd } { -1 } % => d, right-most token
\tl_item:nn { abcd } { -5 } % => nothing: index out of bounds.

(3) If you need a solution which works both for arbitrarily long integers and for older versions of expl3, then you can replace the lines from \newcommand to \prg_case_int:nnn { \int_mod:nn {#1} {10} } by

%----->8---- cut here
\tl_new:N \l_foo_tl
\newcommand {\foo} [2] {
  \tl_clear:N \l_foo_tl
  \tl_map_inline:nn {#1} { \tl_set:Nn \l_foo_tl {##1} }
  \prg_case_int:nnn { \l_foo_tl }
%-----8<---- cut here

The idea is that I go through the string and store each character in \l_foo_tl, until reaching the end of the string. Hence, the last character remains in \l_foo_tl. That's a good replacement for \int_mod:nn {#1} {10} to get the last digit. However, the two other solutions I gave above are better long-term (especially the \tl_item:nn one).

7
  • I chose the easier method; I would probably write two different commands, so the OP can call them with suitable arguments in the main fraction building command.
    – egreg
    Feb 2, 2012 at 16:40
  • @BrunoLeFloch: It looks like this will work for me! (Just did a quick test.) I'm using the MacTeX/TeXLive 2011 distribution. I see that expl3 is intended for LaTeX3. Is it a transitional package that will be obsolete when LaTeX3 is released? Can I consider it to be stable? (Wasn't quite clear to me from the docs.) Also, I would like to understand why the \relax is necessary in the above. Where might I read about that? I've been using LaTeX2e for a while but this is the first LaTeX3 code I've encountered. Feb 2, 2012 at 16:49
  • 1
    @Todd: yes, expl3 is stable, and should remain almost entirely unchanged (except that \usepackage{expl3} won't be needed anymore) in LaTeX3. At the moment, LaTeX3 is expl3 (and some additional packages). We still need quite some time (two to five years, I'd say, but that can't possibly be official) before being able to produce a full format. The \kern ...\relax construction has nothing to do with LaTeX3. It is a subtlety of TeX itself: try \newcommand{\putkern}[1]{\kern#1\relax} then abc\pukern{0pt} def. If you omit the \relax, the space after \putkern{0pt} disappears. Feb 2, 2012 at 16:59
  • @egreg: I don't understand your comment: what did you "choose"? Feb 2, 2012 at 17:00
  • 1
    @ToddLehman No. The :nn syntax tells you how many arguments the function expects. \tl_head:n expects one argument: the token list (or integer in your case) from which to extract the first token. \int_mod:nn expects two arguments: the number and the modulus. Integers in TeX are limited to 32 bits, signed, so roughly 2 billions. In your case, the \tl_item:nn {<your integer>} {-1} solution may be better since it works with tokens (digits) and won't care about the length of the integer. But it is only available in very recent versions of expl3. Feb 2, 2012 at 17:08

You must log in to answer this question.

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