我不明白为什么这段代码的行为是这样的:
\newcommand{\f}{f}% food
\newcommand{\s}{}% stop eating
\newcommand{\h}[1]{h}% hide food
\newcommand{\e}[1]{% eat
\ifx\f#1
e
\else
#1
\fi
}
% f represents food
% e represents eaten food
% [v] means it works as expected
% [x] means it doesn't
% [v] Eat food
[\e\f]% [e]
% [v] Stop eating
[\e\s\f]% [f]
% [v] Hide food
[\h\f]% [h]
% [x] Eat hidden food
[\e\h\f]% [hf]
% I expected [h]
我本以为会这样[\e\h\f]
→ [\h\f]
→ [h]
。我错过了什么?
预先感谢您的帮助。
答案1
如果你\show#1
在你的定义中添加\h
\newcommand{\h}[1]{\show#1h}%
您会看到,在最后一个案例中,的论点\h
不是\f
而是\fi
。
您最好将输出移到\fi
:后面
\documentclass{article}
\begin{document}
\makeatletter
\newcommand{\f}{f}% food
\newcommand{\s}{}% stop eating
\newcommand{\h}[1]{h}% hide food
\newcommand{\e}[1]{% eat
\ifx\f#1\relax
\expandafter\@firstoftwo
\else
\expandafter\@secondoftwo
\fi
{e}%
{#1}%
}
% f represents food
% e represents eaten food
% [v] means it works as expected
% [x] means it doesn't
% [v] Eat food
[\e\f]% [e]
% [v] Stop eating
[\e\s\f]% [f]
% [v] Hide food
[\h\f]% [h]
% [x] Eat hidden food
[\e\h\f]% [hf]
\end{document}
答案2
条件\ifx
比较以下两个标记,如果它们相同,则返回 true \show
。因此,两个相同的字符(按字符代码和类别代码),或两个具有相同状态的宏\long
(\protected
如果使用 e-TeX)和相同的第一级扩展,或相同的 TeX 基元(一个或两个都可以是\let
基元的控制序列)。字符的控制序列\let
将被视为与字符相同。
在 的情况下\e\h\f
,\e
发现它的参数是\h
,因此 TeX 替换前两个标记并得到
\ifx\f\h•e•\else\h•\fi\f
(此处•
表示空格标记)。测试返回 false,因此 TeX 会删除匹配的 之前的所有内容\else
。下一阶段剩下的是
\h•\fi\f
现在\h
发现它的参数是\fi
(查找未分隔的参数时会忽略空格标记),下一个阶段是
h\f
然后 TeX 排版h
,扩展\f
打印f
并警告条件不完整,因为不再有\fi
:
\end occurred when \ifx on line 12 was incomplete
(行号当然会有所不同)。
相反,\e\s\f
会变成
\ifx\f\s•e•\else\s•\fi\f
进而
\s•\fi\f
由于展开式\s
为空,因此得到
•\fi\f
并且空间被排版,则\fi
扩展(不产生任何内容,但让 TeX 对条件计数感到满意)并\f
产生一个“f”。事实上,输出[\e\s\f]
是
[ F]
\e\f
替换后得到
\ifx\f\f•e•\else\f•\fi
并且测试返回 true。在这种情况下,TeX 只是删除\ifx
和测试标记;下一个阶段是
•e•\else\f•\fi
因此打印出“space e space”;然后\else
删除匹配的所有内容\fi
。实际上,的输出[\e\f]
是
[ 电子 ]
您应该对虚假空间以及推挤\else
和\fi
避开障碍物更加小心。
Ulrike 的解决方案很好,没有添加空格。但是,如果你想要一个完全可扩展的宏,请这样做
\newcommand{\e}[1]{% eat
\ifx\f#1%
\expandafter\@firstoftwo
\else
\expandafter\@secondoftwo
\fi
{e}%
{#1}%
}
但一定要确保为 提供一个非空参数\e
。另一种策略是将标记与 进行比较expl3
:
\usepackage{xparse} % loads expl3
\ExplSyntaxOn
\DeclareExpandableDocumentCommand{\e}{m}
{
\token_if_eq_meaning:NNTF \f #1 { e } { #1 }
}
\ExplSyntaxOff
完整代码
\documentclass{article}
\usepackage{xparse}
\newcommand{\f}{f}% food
\newcommand{\s}{}% stop eating
\newcommand{\h}[1]{h}% hide food
\ExplSyntaxOn
\DeclareExpandableDocumentCommand{\e}{m}
{
\token_if_eq_meaning:NNTF \f #1 { e } { #1 }
}
\ExplSyntaxOff
\begin{document}
% f represents food
% e represents eaten food
% Eat food
[\e\f]% [e]
% Stop eating
[\e\s\f]% [f]
% Hide food
[\h\f]% [h]
% Eat hidden food
[\e\h\f]% [h]
\end{document}