进食命令

进食命令

我不明白为什么这段代码的行为是这样的:

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

相关内容