6

I would like to write a latex macro \p which given input X returns X or (X) depending on whether X has character length 1 or not. Moreover, I want to be able to nest applications of \p. E.g. I want the output:

\p{aa} = (aa)
\p{a} = a
\p{aa\p{b}}=(aab)
\p{\p{a}}=(a)

Here is what I have so far. This creates an error message. I believe this is because \StrLen cannot be nested.

\documentclass{article}
\usepackage{ifthen,xstring}

\newcommand{\p}[1]{
\StrLen{#1}[\Len]
\ifthenelse{\Len=1}{#1}{(#1)}
}

\begin{document}

$\p{aa\p{b}}$

\end{document}

So this question is essentially about identifying the (raw) input to a macro. What is the simplest solution (using any suitable packages)?

Revantha.

  • Why should \p{\p{a}} yield (a) instead of a? After all, with \p{a}=a we have \p{\p{a}}=\p{a}=a – Ulrich Diez Jul 8 '18 at 1:51
  • For my task, it sufficed to differentiate based on the raw input (i.e. including the markup). So in \p{\p{a}}, the inner call to \p has input a so its output should be a. Meanwhile the outer call has input \p{a} (and not a, since I am referring to raw input) and therefore it must add parentheses. – Rev Jul 8 '18 at 8:23
  • Okay, I can edit my answer in order to take that into account. But what's about the case of the argument of \p being empty, i.e., \p{} or nested calls to \p where inner calls to \p are provided empty arguments, i.e., \p{\p{}}? – Ulrich Diez Jul 8 '18 at 9:52
  • You wrote \p{aa\p{b}}=(aab). Does the order in time matter in which the result is obtained? I.e., is \p{aa\p{b}} -> (aa\p{b}) sufficient, where obtaining the result of the inner \p{b} is not part of the process of obtaining the result of the outer \p{...}? If this is not sufficient but processing outer calls to \p must also deliver the result of inner calls to \p, so that, e.g., in any case the entire result is obtained after two hits with \expandafter, that can be achieved by recursion at the cost of replacing any explicit catcode1/2 character token by opening/closing braces. – Ulrich Diez Jul 8 '18 at 13:27
5

I think that if you turn off the expansion of the argument of \StrLen with \noexpandarg it passes your tests:

enter image description here

\documentclass{article}
\usepackage{ifthen,xstring}

\newcommand{\p}[1]{%
  \begingroup
    \noexpandarg
    \StrLen{#1}[\Len]%
    \ifthenelse{\Len=1}{#1}{(#1)}%
  \endgroup}

\begin{document}

\(\p{aa} = (aa)\)

\(\p{a} = a\)

\(\p{aa\p{b}}=(aab)\)

\(\p{\p{a}}=(a)\)

\end{document}

I nested the \noexpandarg in a group as egreg suggested. I opted to add it inside the definition because this expansion behavior is not always wanted, but will be the case when \p is called.

  • Great! Now \p{aa\p{bb}}=(aa(bb)) exactly as I wanted. – Rev Jul 7 '18 at 19:01
  • Either add \begingroup and \endgroup or just issue \noexpandarg in the preamble: it's useless to issue it at every call. – egreg Jul 8 '18 at 14:07
  • @egreg Well pointed, Grazie :) – Phelype Oleinik Jul 8 '18 at 14:26
5

No need for packages.

\documentclass{article}
\newcommand{\p}[1]{\paux#1\endp}
\def\paux#1#2\endp{\ifx\relax#2\relax#1\else(#1#2)\fi}
\begin{document}
\(\p{aa} = (aa)\)

\(\p{a} = a\)

\(\p{aa\p{b}}=(aab)\)

\p{aa\p{bb}} = (aa(bb)) 

\(\p{\p{a}}=(a)\)

\end{document}

enter image description here

  • That's a neat solution. My understanding is that \paux#1#2 pattern matches the first input character with #1 and the remainder with #2. Then \ifx\relax#2 tests if #2 evaluates to 'nothing'. The second \relax in \ifx\relax#2\relax#1 is simply to demarcate the end of the \ifx test. Fine. Could someone explain the role of \endp and why \def is used instead of \newcommand ? – Rev Jul 8 '18 at 8:12
  • @Rev Be aware that with this solution a leading space token within \p's argument is not taken into account while a trailing space token is taken into account, i.e., \p{ a} yields a while \p{a } yields (a ). Curly braces inside the argument in some places might be stripped off without being taken into account---e.g. \p{{a}{}} yields the catcode11-character-token a. Is this the desired result ? Do you prefer getting the tokens ({a}{})? How to handle cases with curly braces and/or empty arguments and or space tokens in the arguments? – Ulrich Diez Jul 8 '18 at 12:39
  • 1
    @Rev 1) The \endp is introduced to demarcate the end of the remainder of the original argument, without knowing in advance how long it is. 2) \def is used because \paux is looking for an input of the form #1#2\endp, which cannot be achieved with the \newcommand syntax. 3) Ulrich's warnings are all valid, and may or may not be an issue, depending on how you plan to use he macro. – Steven B. Segletes Jul 8 '18 at 14:40
  • Please, don't call my utterances "warnings". Warnings are frightening. I did not intend to scare people. I just tried to draw attention towards some facts. ;-) By the way: I recommend not to use the token\endp inside \p's argument. In order to have no restriction on the set of tokens that can be used, I tried to implement something that does with brace hacks rather than with delimited arguments. ;-) For the interested: Recently there was a thread about how TeX treats (un)delimited macro arguments. – Ulrich Diez Jul 8 '18 at 16:35
  • 1
    @Rev The parameter text of the definition of \paux is #1#2\endp. #1 is an undelimited argument. #2 is a delimited argument whose delimiter is \endp. You can't define macros with delimited arguments via \newcommand but need to use one of the primitives \def / \gdef / \edef / \xdef instead. In case you are interested: Recently there was a thread about how TeX treats (un)delimited macro arguments. – Ulrich Diez Jul 8 '18 at 17:17
3

The mandatory expl3 version:

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn

\NewExpandableDocumentCommand{\p}{m}
 {
  \rev_p:n { #1 }
 }

\cs_new:Nn \rev_p:n
 {
  \int_compare:nTF { \tl_count:n { #1 } > 1 } { (#1) } { #1 }
 }

\ExplSyntaxOff

\begin{document}

\begin{tabular}{ll}
Input       & Should be \\
\hline
\p{aa}      & (aa) \\
\p{a}       & a \\
\p{aa\p{b}} & (aab) \\
\p{aa\p{bb}} & (aa(bb)) \\
\p{\p{a}}   & (a) \\
\end{tabular}

\end{document}

enter image description here

2

What does "character length" mean? (By now I only heard about the term "string length".)

Why should \p{\p{a}} yield (a) instead of a?
After all, with \p{a}=a we have \p{\p{a}}=\p{a}=a.

Yet nobody explicitly mentioned the case of the string not having any character at all/of the length of the string in question being 0.
In this case the condition of the length of the string in question not being 1 is satisfied as well.

Also the question arises how spaces and things nested in curly braces should be treated.

By now I decided to implement a variant where

  • delivering an empty argument to \p will deliver a pair of parentheses as in this case the condition of the string-length not being 1 is fulfilled as well.
  • expandable tokens within the argument are not expanded.
  • actually not the amount of character tokens of category code 11 and 12 is checked but the amount of tokens is checked—be it whatsoever character tokens, be it curly braces, be it space tokens, be it whatsoever control sequence tokens.

The macros themselves do neither require ε-TeX extensions nor require the array package.
Within the example below I used these things just for illustrating the way in which things work.

\documentclass{article}
\usepackage{array}
\makeatletter
%%=========================================================================
%% Paraphernalia:
\newcommand\UD@firstoftwo[2]{#1}%
\newcommand\UD@secondoftwo[2]{#2}%
\newcommand\UD@Exchange[2]{#2#1}%
\newcommand\UD@removespace{}\UD@firstoftwo{\def\UD@removespace}{} {}%
%%----------------------------------------------------------------------
%% Check whether argument is empty:
%%......................................................................
%% \UD@CheckWhetherNull{<Argument which is to be checked>}%
%%                     {<Tokens to be delivered in case that argument
%%                       which is to be checked is empty>}%
%%                     {<Tokens to be delivered in case that argument
%%                       which is to be checked is not empty>}%
%%
%% The gist of this macro comes from Robert R. Schneck's \ifempty-macro:
%% <https://groups.google.com/forum/#!original/comp.text.tex/kuOEIQIrElc/lUg37FmhA74J>
%%
\newcommand\UD@CheckWhetherNull[1]{%
  \romannumeral0\expandafter\UD@secondoftwo\string{\expandafter
  \UD@secondoftwo\expandafter{\expandafter{\string#1}\expandafter
  \UD@secondoftwo\string}\expandafter\UD@firstoftwo\expandafter{\expandafter
  \UD@secondoftwo\string}\expandafter\expandafter\UD@firstoftwo{ }{}%
  \UD@secondoftwo}{\expandafter\expandafter\UD@firstoftwo{ }{}\UD@firstoftwo}%
}%
%%----------------------------------------------------------------------
%% Check whether argument's first token is a catcode-1-character
%%......................................................................
%% \UD@CheckWhetherBrace{<Argument which is to be checked>}%
%%                      {<Tokens to be delivered in case that argument
%%                        which is to be checked has leading
%%                        catcode-1-token>}%
%%                      {<Tokens to be delivered in case that argument
%%                        which is to be checked has no leading
%%                        catcode-1-token>}%
\newcommand\UD@CheckWhetherBrace[1]{%
  \romannumeral0\expandafter\UD@secondoftwo\expandafter{\expandafter{%
  \string#1.}\expandafter\UD@firstoftwo\expandafter{\expandafter
  \UD@secondoftwo\string}\expandafter\expandafter\UD@firstoftwo{ }{}%
  \UD@firstoftwo}{\expandafter\expandafter\UD@firstoftwo{ }{}\UD@secondoftwo}%
}%
%%----------------------------------------------------------------------
%% Check whether argument's leading tokens form a specific 
%% token sequence that does not contain explicit character tokens of 
%% category code 1 or 2:
%%......................................................................
%% \UD@CheckWhetherLeadingTokens{<<token sequence> without explicit
%%                           character tokens of category code 1 or 2>}%
%%                           {<a single non-space token> that does 
%%                            _not_ occur in <token sequence>>}%
%%                           {<internal token-check-macro>}%
%%                           {<argument which is to be checked>}%
%%                           {<tokens to be delivered in case <argument
%%                             which is to be checked> has <token sequence>
%%                             as leading tokens>}%
%%                           {<tokens to be delivered in case <argument
%%                             which is to be checked> does not have
%%                             <token sequence> as leading tokens>}%
\newcommand\UD@CheckWhetherLeadingTokens[4]{%
  \romannumeral0\UD@CheckWhetherNull{#4}%
  {\expandafter\expandafter\UD@firstoftwo{ }{}\UD@secondoftwo}%
  {\expandafter\UD@secondoftwo\string{\expandafter
   \UD@@CheckWhetherLeadingTokens#3#2#4#1}{}}%
}%
\newcommand\UD@@CheckWhetherLeadingTokens[1]{%
  \expandafter\UD@CheckWhetherNull\expandafter{\UD@firstoftwo{}#1}%
  {\UD@Exchange{\UD@firstoftwo}}{\UD@Exchange{\UD@secondoftwo}}%
  {\UD@Exchange{ }{\expandafter\expandafter\expandafter\expandafter
   \expandafter\expandafter\expandafter}\expandafter\expandafter
   \expandafter}\expandafter\UD@secondoftwo\expandafter{\string}%
}% 
%%----------------------------------------------------------------------
%% \UD@internaltokencheckdefiner{<internal token-check-macro>}%
%%                              {<token sequence>}%
%% Defines <internal token-check-macro> to snap everything 
%% until reaching <token sequence> and spit that out nested in
%% braces.
%%......................................................................
\newcommand\UD@internaltokencheckdefiner[2]{%
  \newcommand#1{}\long\def#1##1#2{{##1}}%
}%
\UD@internaltokencheckdefiner{\UD@CheckSp}{ }%
% In case you wish to have TeX take into account nestet calls to `\p` as well,
% "uncomment" the following line, and see the comments in the definition of \p.
%\UD@internaltokencheckdefiner{\UD@CheckP}{\p}%
%%-----------------------------------------------------------------------------
%% Check whether undelimited argument consists of exactly one token.
%%.............................................................................
%% \UD@CheckWhetherSingleToken{<Argument which is to be checked>}%
%%                            {<Tokens to be delivered in case <argument
%%                               which is to be checked>consists of a single
%%                               token>}%
%%                            {<Tokens to be delivered in case <argument
%%                               which is to be checked>does not
%%                               consist of a single token>}%
\newcommand\UD@CheckWhetherSingleToken[1]{%
%  \romannumeral0%
  \UD@CheckWhetherNull{#1}{\UD@secondoftwo}{%
    \UD@CheckWhetherBrace{#1}{\UD@secondoftwo}{%
      \UD@CheckWhetherLeadingTokens{ }{.}{\UD@CheckSp}{#1}{%
        \expandafter\UD@CheckWhetherNull\expandafter{\UD@removespace#1}%
      }{%
        \expandafter\UD@CheckWhetherNull\expandafter{\UD@firstoftwo{}#1}%
      }%
      {\UD@firstoftwo}{\UD@secondoftwo}%
    }%
  }%
%  {\expandafter\expandafter\UD@firstoftwo{ }{}\UD@firstoftwo}%
%  {\expandafter\expandafter\UD@firstoftwo{ }{}\UD@secondoftwo}%
}%
%%-----------------------------------------------------------------------------
%% The desired command \p:
%%   \p{<Argument which is to be checked>} 
%% in case <Argument which is to be checked> after expanding an internal 
%% leading \p{..} consists of a single token delivers:
%%   <Argument which is to be checked>
%% in case <Argument which is to be checked> after expanding an internal 
%% leading \p{..} does not consist of a single token delivers:
%%   <Argument which is to be checked>
%%.............................................................................
\newcommand\p[1]{%
  \romannumeral0%
% "uncomment" the commented lines in case you wish to have TeX take into
% account nested calls to `\p` as well.
%  \UD@CheckWhetherLeadingTokens{\p}{.}{\UD@CheckP}{#1}{%
%    \expandafter\expandafter\expandafter\UD@CheckWhetherSingleToken
%     \expandafter\expandafter\expandafter{#1}{%
%       \expandafter\expandafter\expandafter
%       \expandafter\expandafter\expandafter\UD@firstoftwo{ }{}#1%
%     }{%
%       \expandafter\expandafter\expandafter
%       \expandafter\expandafter\expandafter\UD@firstoftwo{ }{}%
%       \expandafter\expandafter\expandafter(#1)%
%     }%
%  }{%
    \UD@CheckWhetherSingleToken{#1}{ #1}{ (#1)}%
%  }%
}%
\makeatother

\textwidth=\paperwidth
\advance\textwidth-4cm
\oddsidemargin=2cm
\advance\oddsidemargin-1in
\advance\oddsidemargin-\hoffset
\evensidemargin=\oddsidemargin

\begin{document}

\begin{tabular}{|l|l|l|}
\hline
Sequence&\multicolumn{1}{p{3cm}|}{\hbox{Total expansion\(=\)}\hbox{2\(^{\mathrm{nd}}\)-level-expansion} of outermost \texttt{\string\p}}&Result\\\hline
\verb*+\p{a}+& 
\expandafter\verb\expandafter*\expandafter|\scantokens\expandafter\expandafter\expandafter{\p{a}|}&
\p{a}\\\hline
\verb*+\p{\p{a}}+&
\expandafter\verb\expandafter*\expandafter|\scantokens\expandafter\expandafter\expandafter{\p{\p{a}}|}&
\p{\p{a}}\\\hline
\verb*+\p{\p{\p{a}}}+&
\expandafter\verb\expandafter*\expandafter|\scantokens\expandafter\expandafter\expandafter{\p{\p{\p{a}}}|}&
\p{\p{\p{a}}}\\\hline
\verb*+\p{{a}}+&
\expandafter\verb\expandafter*\expandafter|\scantokens\expandafter\expandafter\expandafter{\p{{a}}|}&
\p{{a}}\\\hline
\verb*+\p{aa}+&
\expandafter\verb\expandafter*\expandafter|\scantokens\expandafter\expandafter\expandafter{\p{aa}|}&
\p{aa}\\\hline
\verb*+\p{aa\p{b}}+&
\expandafter\verb\expandafter*\expandafter|\scantokens\expandafter\expandafter\expandafter{\p{aa\p{b}}|}&
\p{aa\p{b}}\\\hline
\verb*+\p{aa\p{bb}}+&
\expandafter\verb\expandafter*\expandafter|\scantokens\expandafter\expandafter\expandafter{\p{aa\p{bb}}|}&
\p{aa\p{bb}}\\\hline
\verb*+\p{}+& 
\expandafter\verb\expandafter*\expandafter|\scantokens\expandafter\expandafter\expandafter{\p{}|}&
\p{}\\\hline
\verb*+\p{\p{}}+&
\expandafter\verb\expandafter*\expandafter|\scantokens\expandafter\expandafter\expandafter{\p{\p{}}|}&
\p{\p{}}\\\hline
\verb*+\p{\p{aa}\p{bb}}+&
\expandafter\verb\expandafter*\expandafter|\scantokens\expandafter\expandafter\expandafter{\p{\p{aa}\p{bb}}|}&
\p{\p{aa}\p{bb}}\\\hline
\verb*+\p{}+&
\expandafter\verb\expandafter*\expandafter|\scantokens\expandafter\expandafter\expandafter{\p{}|}&
\p{}\\\hline
\verb*+[\p{ }]+&
\expandafter\verb\expandafter*\expandafter|\expandafter[\scantokens\expandafter\expandafter\expandafter{\p{ }]|}&
[\p{ }]\\\hline
\verb*+\p{ a}+&
\expandafter\verb\expandafter*\expandafter|\scantokens\expandafter\expandafter\expandafter{\p{ a}|}&
\p{ a}\\\hline
\verb*+\p{\p{ } }+&
\expandafter\verb\expandafter*\expandafter|\scantokens\expandafter\expandafter\expandafter{\p{\p{ } }|}&
\p{\p{ } }\\\hline
\verb*+\p{a{}}+&
\expandafter\verb\expandafter*\expandafter|\scantokens\expandafter\expandafter\expandafter{\p{a{}}|}&
\p{a{}}\\\hline
\end{tabular}

\end{document}

enter image description here

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.