考虑以下代码
\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
比较了标记,因此这里进行比较A
:b
如果您的第一个字母是大写而第二个字母是小写,那么它永远不会成立。
据我所知,您不需要空格分隔的参数,您只需迭代即可?
。如果您的输入没有 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
比较,并取错误分支。A
b
但如果你用的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}