使用 xstring ifthen 包嵌套宏识别字符串长度 1

使用 xstring ifthen 包嵌套宏识别字符串长度 1

我想编写一个 latex 宏\p,给定输入 X,根据 X 的字符长度是否为 1 返回 X 或 (X)。此外,我希望能够嵌套应用程序\p。例如,我想要输出:

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

这是我目前所得到的。这会产生一条错误消息。我相信这是因为\StrLen无法嵌套。

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

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

\begin{document}

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

\end{document}

因此,这个问题本质上是关于识别宏的(原始)输入。最简单的解决方案是什么(使用任何合适的包)?

瑞凡莎。

答案1

我认为如果你关闭参数的扩展,\StrLen它就\noexpandarg可以通过你的测试:

在此处输入图片描述

\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}

我按照 egreg 的建议将 嵌套\noexpandarg在一个组中。我选择将其添加到定义中,因为这种扩展行为并不总是\p想要,但是当被调用时就会出现这种情况。

答案2

不需要包裹。

\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}

在此处输入图片描述

答案3

强制expl3版本:

\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}

在此处输入图片描述

答案4

“字符长度”是什么意思?(到目前为止我只听说过“字符串长度”这个术语。)

为什么应该用\p{\p{a}}yield(a)代替a
毕竟,有了\p{a}=,我们a就有了\p{\p{a}}== \p{a}a

但是没有人明确提到字符串根本没有任何字符/所讨论的字符串的长度为 0 的情况。
在这种情况下,所讨论的字符串的长度不为 1 的条件也得到满足。

另外还出现了一个问题,即应如何处理空格和嵌套在花括号中的东西。

现在我决定实现一个变体,其中

  • 传递一个空参数将会\p传递一对括号,因为在这种情况下,字符串长度不为 1 的条件也得到满足。
  • 参数内的可扩展标记不会被扩展。
  • 实际上,并不是检查类别代码 11 和 12 的字符标记的数量,而是检查标记的数量 — — 无论是字符标记、花括号、空格标记还是控制序列标记。

宏本身不需要ε-TeX扩展也不需要大批包。
在下面的例子中,我使用这些东西只是为了说明事物的工作方式。

\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}

在此处输入图片描述

相关内容