重新审视递归宏

重新审视递归宏

考虑以下代码

\documentclass{article}
\usepackage{xifthen}

\def\question#1 #2 #3?{\ifx#1 #2 #3?\relax\else#1 #2 #3 or\expandafter\question\fi}
\newcommand{\test}[1]{\question#1}
\begin{document}
\test{Ab Bc Cd? Tm Br Sl? Kr Nd Zf?}
\end{document}

输出为

Ab Bc Cd or Tm Br Sl or Kr Nd Zf or

并出现错误:“段落在问题完成之前结束。”

如何改进这一点,使得最后的“或”不打印?

目标:“Ab Bc Cd 或 Tm Br Sl 或 Kr Nd Zf”等等,以实现更复杂的模式匹配。

如何改进此代码以纠正错误?

相关问题:

\def\splitter#1 #2 #3?#4{#1 #2 #3\ifthenelse{\isempty{#4}}{}{ \(|\) \splitter#4}}

效果更糟,最后三个词条不见了,还出现了换行符,还出现了错误?为什么?

我尝试阅读一本关于递归宏的 TeX 相关书籍,但他们建议对参数进行计数,或者采用类似于第二种情况的形式,而在某些地方插入 relax 似乎不起作用。

答案1

在这种情况下,在数据中添加终止符会有所帮助\endq,因为它消除了向前查看(可能使用\futurelet)来查找数据流末尾的需要。

此外,定义中的终止符\test可以消除歧义,例如,如果执行以下操作就会出现这种情况:

\test{Ab Bc Cd? Tm Br Sl? Kr Nd Zf?} Am I done?

妇女权利委员会:

\documentclass{article}
\newif\ifneedor
\def\question#1 #2 #3? #4\endq{%
  \ifx\relax#4\relax
    \def\next{}%
  \else
    \def\next{\question#4\endq}%
  \fi
  \ifneedor\ or \else\needortrue\fi
  #1 #2 #3\next}
\newcommand{\test}[1]{\needorfalse\question#1 \endq}
\begin{document}
\test{Ab Bc Cd? Tm Br Sl? Kr Nd Zf?}.
\end{document}

在此处输入图片描述

下面是消除的另一种形式\ifneedor

\documentclass{article}
\def\question#1 #2 #3? #4\endq{%
  #1 #2 #3%
  \ifx\relax#4\relax
    \def\next{}%
  \else
    \ or \def\next{\question#4\endq}%
  \fi
  \next}
\newcommand{\test}[1]{\question#1 \endq}
\begin{document}
\test{Ab Bc Cd? Tm Br Sl? Kr Nd Zf?}.
\end{document}

答案2

我认为你的主要错误是

\ifx#1

目前尚不清楚您想要比较什么,但\ifx比较了标记,因此这里进行比较Ab如果您的第一个字母是大写而第二个字母是小写,那么它永远不会成立。

据我所知,您不需要空格分隔的参数,您只需迭代即可?。如果您的输入没有 final,那会更容易,?但这会在额外的步骤中将其删除。

在此处输入图片描述

\documentclass{article}


\def\question#1?\xquestion{\xquestion#1\yquestion?}
\def\xquestion#1?{#1 or\xquestion}
\def\yquestion#1#2#3{}

\newcommand{\test}[1]{\question#1\xquestion}

\begin{document}
\test{Ab Bc Cd? Tm Br Sl? Kr Nd Zf?}
\end{document}

答案3

抱歉,但您的代码不仅仅是因为缺少终止而错误。

让我们看看你的电话发生了什么。来自

\test{Ab Bc Cd? Tm Br Sl? Kr Nd Zf?}

我们得到

\question Ab Bc Cd? Tm Br Sl? Kr Nd Zf?

现在\question获取分隔参数,在这种情况下你会得到

#1 <- Ab
#2 <- Bc
#3 <- Cd

输入流将变成

\ifx Ab Bc Cd?\relax\else Ab Bc Cd or\expandafter\question\fi Tm Br Sl? Kr Nd Zf?

现在与之\ifx比较,并取错误分支。Ab

但如果你用的AA是,情况会怎样Ab

在这种情况下,错误的分支将被跳过,你会得到

 Bc Cd? Tm Br Sl? Kr Nd Zf?

根本没有递归。

?如果您的目的是在中间用“或”替换,那么您需要一种完全不同的方法。

最简单的一个:

\documentclass{article}

\ExplSyntaxOn

\NewDocumentCommand{\test}{m}{%
  \seq_set_split:Nnn \l_tmpa_seq { ? } { #1 }
  % check for a trailing ? which is present if the last item is empty
  \seq_pop_right:NN \l_tmpa_seq \l_tmpa_tl
  \tl_if_empty:NF \l_tmpa_tl
   {% no trailing ?, restore the item
    \seq_put_right:NV \l_tmpa_seq \l_tmpa_tl
   }
  \seq_use:Nn \l_tmpa_seq { ~or~ }
 }

\ExplSyntaxOff

\begin{document}

\test{Ab Bc Cd? Tm Br Sl? Kr Nd Zf?}

\test{Ab Bc Cd? Tm Br Sl? Kr Nd Zf}

\end{document}

在此处输入图片描述

答案4

以下类似于@egreg 的答案,但它不是将输入分解为序列并从该序列重建标记列表,而是直接通过 对输入进行操作\tl_replace_all:Nnn

这与@egreg 的答案相反:如果您不在后面加空格?或者在前面加空格?,则会出现错误,后面没有空格or或者前面有太多空格。@egreg 的答案将删除周围的所有空格?(以及任何前导或尾随的空格),然后正确插入它们({ ~or~ })。

\documentclass[border=3.14, varwidth]{standalone}

\ExplSyntaxOn
\tl_new:N \l_vaduzstevin_questions_tl
\cs_new_protected:Npn \vaduzstevin_questions:n #1
  {
    % place a marker at the end so that we can distinguish the last ?
    \tl_set:Nn \l_vaduzstevin_questions_tl { #1 \s_stop }
    % remove the last ? if it's there
    \tl_replace_all:Nnn \l_vaduzstevin_questions_tl { ? \s_stop } { \s_stop }
    % remove the marker (we do this in two steps to also get it correct if there
    % is no trailing ? in the input)
    \tl_replace_all:Nnn \l_vaduzstevin_questions_tl { \s_stop } {}
    % replace all other ? with ' or'
    \tl_replace_all:Nnn \l_vaduzstevin_questions_tl { ? } { ~ or }
    \l_vaduzstevin_questions_tl
  }
\NewDocumentCommand \test { m }
  { \vaduzstevin_questions:n {#1} }
\ExplSyntaxOff

\begin{document}
\test{Ab Bc Cd? Tm Br Sl? Kr Nd Zf?}

\test{Ab Bc Cd? Tm Br Sl? Kr Nd Zf}
\end{document}

在此处输入图片描述

相关内容