我想编写一个 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
答案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}