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}

\p{\p{a}}
yield(a)
instead ofa
? After all, with\p{a}
=a
we have\p{\p{a}}
=\p{a}
=a
\p
being empty, i.e.,\p{}
or nested calls to\p
where inner calls to\p
are provided empty arguments, i.e.,\p{\p{}}
?\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.