如何\if
在核心 TeX 中制作自定义变体?
我想写:
\ifshape{it} ... \else ... \fi
例如:
\textit{Italic? \ifshape{it}Yes\else No\fi}
\ifshape
问题是,当我定义为时,出现错误“太多}”
\makeatletter
\newcommand{\ifshape}[1]{%
\def\ifshape@test{#1}%
\ifx\f@shape\ifshape@test%
}
\makeatother
构造\if...\else...\fi
是否真的具有词法作用域?我不认为 (La)TeX 具有词法作用域。我期望上述内容(定义加调用)扩展为:
\def\ifshape@test{it}\ifx\f@shape\ifshape@test Yes\else No\fi
当然,如果我这样写,效果会很好:
\makeatletter
\newcommand{\ifshapethenelse}[3]{%
\def\ifshape@test{#1}%
\ifx\f@shape\ifshape@test{#2}\else{#3}\fi%
}
\makeatother
但是我想使用中缀表示法而不是前缀表示法来写,因为现在@DavidCarlisle 已经向我指出\ifthenelse
(来自包ifthen
)是一个中缀怪物并且实际上并没有做任何神奇的事情。
我是否需要在某处插入\relax
和\noexpand
的某种组合才能使其工作?
答案1
问题在于的扩展\textit
以条件开始:
\ifmmode
\nfss@text {\itshape #1}%
\else
\hmode@bgroup\text@command{#1}\itshape\check@icl
#1%
\check@icr\expandafter\egroup
\fi
让我们看看当你提出#1
论点时会发生什么:
\ifmmode
\nfss@text {\itshape Italic? \ifshape{it}Yes\else No\fi}%
\else
\hmode@bgroup\text@command{Italic? \ifshape{it}Yes\else No\fi}\itshape\check@icl
Italic? \ifshape{it}Yes\else No\fi
\check@icr\expandafter\egroup
\fi
问题在于:你的参数的\else
and匹配:你不在数学模式中,因此会遵循“false”路径,然后找到多余的右括号。宏是\fi
\ifmmode
\ifshape
不是扩展,因为它处于“真实”路径中。
解决这个问题的通常方法是
\makeatletter
\newcommand{\ifshape}[1]{%
\def\f@shapetest{#1}%
\ifx\f@shape\f@shapetest
\expandafter\@firstoftwo
\else
\expandafter\@secondoftwo
\fi}
\makeatother
并称之为
\textit{Italic? \ifshape{it}{Yes}{No}}
在这种情况下,直到扩展时才会看到\else
或,并且它们将正确引用。\fi
\ifshape
\ifx
另一种方法是说
\makeatletter
\newcommand\isshape[1]{%
TT\fi
\def\f@shapetest{#1}%
\ifx\f@shape\f@shapetest}
\makeatother
可以称之为
\textit{Italic? \if\isshape{it}Yes\else No\fi}
这是一个值得仔细研究的技巧(它是由大魔法师自己设计的)。
这个技巧是什么?它基于这样的事实:\if
想要在它后面找到两个不可扩展的标记。当\if
在扩展上下文中找到时,它会扩展\isshape
并立即发现TT
相等,因此 TeX 会遵循“真”分支,该分支为空,而后面的分支\fi
会消失。如果改用TL
,TeX 会遵循“假”分支,该分支也是空的!
接下来,可能在其他标记之后,是真正让我们关心的条件,它会像往常一样进行评估。
如果\if\isshape
在非扩展上下文中发现 ,例如在另一个条件的“假”分支中,我们打算执行内部条件的工作的\else
和让 TeX 满意,因为它们与“假” 配对。\fi
\if
唯一重要的是这两个令牌是不可扩展的,因此\relax\relax
或\hfuzz\uchyph
也可以起作用。
答案2
如果你愿意将测试的语法稍微改变一下\ifshape{it}\then Yes\else No\fi
,你也可以使用 Donald Arseneau 提出的以下技巧(来自\ifnum 表示实数comp.text.tex 上的线程) :
\documentclass{article}
\makeatletter
\let\then\iffalse
\def\ifshape#1\then{%
\def\ifshape@test{#1}%
\ifx\f@shape\ifshape@test
}
\makeatother
\begin{document}
\textit{Italic? \ifshape{it}\then Yes\else No\fi}
\textbf{Italic? \ifshape{it}\then Yes\else No\fi}
\end{document}