\ifnot 宏的更优雅版本

\ifnot 宏的更优雅版本

拖船文章15 年前提到了\ifnotDavid Kastrup 的一个宏,其实现如下:

\def\ifnot#1{#1\else
    \expandafter\expandafter\fi
    \iffalse\iftrue\fi}

但是,当这个宏应用于\ifSomething宏时,它看起来有点奇怪,即\ifnot{\ifeof\stream}。为了使 TeX 代码更具可读性,使用一个宏来否定“条件”本身(不带前缀)可能会很有用if

这是一个最小的例子,但它不起作用。

\documentclass{独立}
\制作字母
\newif\如果@to@be
\开始{文档}
  \@是真实的
  \if@not\@to@be@ 不是!\fi
  \@to@be@false
  \if@not\@to@be@ 不是!\fi
\结束{文档}

答案1

可能是这样的吗?它保留了 OP 所需的语法\ifnot\tobe,同时又不要求\tobe预定义。此外,对于那些不喜欢\tobe在未定义的情况下使用的人,它允许使用替代语法\ifnot{tobe},而无需进行任何更改。

\documentclass{article}
\makeatletter
% Following 3 lines thanks to Prof. Enrico Gregorio, from:
% http://tex.stackexchange.com/questions/42318/
% removing-a-backslash-from-a-character-sequence
\begingroup\lccode`\|=`\\
\lowercase{\endgroup\def\removebs#1{\if#1|\else#1\fi}}
\newcommand{\@macro@name}[1]{\expandafter\removebs\string#1}
%
\def\ifnot#1{%
  \edef\tmp{if\@macro@name{#1}}%
  \csname\tmp\endcsname\else
    \expandafter\expandafter\fi
    \iffalse\iftrue\fi}
\makeatother
\newif\iftobe
\begin{document}
  \tobetrue
  \ifnot\tobe Not to be! \else To be!\fi\par
  \tobefalse
  \ifnot\tobe Not to be! \else To be!\fi
\end{document}

在此处输入图片描述

答案2

使用 e-TeX 并假设\escapechar可打印且不是空格:

\documentclass{standalone}
\makeatletter
\newif\if@to@be@
\def\if@not#1{%
  \expandafter\unless\csname
    \expandafter\expandafter\expandafter i%
    \expandafter\expandafter\expandafter f%
      \expandafter\@gobble\string#1\endcsname
}
\begin{document}
  \@to@be@true
  \if@not\@to@be@ Not to be! \fi
  \@to@be@false
  \if@not\@to@be@ Not to be! \fi
\end{document}

\escapechar(如有需要,可以解除的限制:请\cs_to_str:N参阅expl3。)

答案3

参数中必须具有完整的条件\ifnot对于宏的工作至关重要,否则它不能出现在另一个条件中,因为 TeX 会跟踪跳过的文本中的\if...\else和。\fi

假设条件\iftobe已定义,则您的宏应像

\ifnot{tobe}Not to be\else To be\fi

现在让我们尝试一下

\iftrue
  \ifnot{tobe}Not to be\else To be\fi
\fi

而不是\iftrue考虑任何其他测试,例如\ifdim\maxdimen>0pt,返回 true。这不会带来任何问题,因为测试被删除并\ifnot扩展,恢复\iftobe将与第一个匹配的\fi

现在考虑

\iffalse
  \ifnot{tobe}Not to be\else To be\fi
\fi

\else测试结果为假,因此跳过匹配(或)之前的所有内容\fi。好吧,有\else,所以To be\fi\fi输入流中仍保留 。你看到问题了吗?有一个不匹配的\fi

为宏指定以 开头的名称\if不会使其成为条件。只有控制序列符合\let原始条件才会计数。因此 TeX 不会考虑跳过的文本与或\ifnot匹配。\else\fi

您必须使用真正的条件:

\newif\iftobe

\def\NOT#1{%
  TT\fi
  \csname if#1\endcsname\else
  \expandafter\expandafter\fi
  \iffalse\iftrue\fi
}

\tobetrue

\if\NOT{tobe}Not to be\else To be\fi

\tobefalse

\if\NOT{tobe}Not to be\else To be\fi

\bye

在此处输入图片描述

作为练习,尝试

\iffalse\if\NOT{tobe}Not to be\else To be\fi\fi

并且没有看到出现任何错误。

结果与

\newif\iftobe

\def\ifnot#1{#1\else
  \expandafter\expandafter\fi
  \iffalse\iftrue\fi
}

\tobetrue

\ifnot{\iftobe}Not to be\else To be\fi

\tobefalse

\ifnot{\iftobe}Not to be\else To be\fi

\bye

当然,David Kastrup 的宏功能更强大,因为你可以在参数中使用任何条件,例如

\ifnot{\ifdim\maxdimen>0pt}TRUE\else FALSE\fi

将打印 FALSE。

当然,使用 e-TeX 会更容易:

\unless\ifdim\maxdimen>0pt TRUE\else FALSE\fi

也会做同样的事。


在我看来,这是一种解除限制的实现,但根本没用。诀窍是使其\tobe等同于\iffalse,这样当构造位于条件的跳过文本中时,它将被计算在内\ifnot\tobe。当然,\tobe不建议在野外使用。;-)

\documentclass{article}
\usepackage{expl3}

\ExplSyntaxOn

\cs_new_protected:Npn \newifnegatable #1
 {
  \exp_args:Nc \newif { if \cs_to_str:N #1 }
  \cs_set_eq:Nc #1 { if_false: }
 }

\cs_new:Npn \ifnot #1
 {
  \use:c { if \cs_to_str:N #1 }
  \else:
  \exp_after:wN \exp_after:wN \fi:
  \if_false: \if_true: \fi:
}

\ExplSyntaxOff

\newifnegatable\tobe

\begin{document}

\tobetrue

\ifnot\tobe Not to be\else To be\fi

\iftrue\ifnot\tobe Not to be\else To be\fi\fi

\iffalse\ifnot\tobe Not to be\else To be\fi\fi

\tobefalse

\ifnot\tobe Not to be\else To be\fi

\iftrue\ifnot\tobe Not to be\else To be\fi\fi

\iffalse\ifnot\tobe Not to be\else To be\fi\fi

\end{document}

在此处输入图片描述

一个可能更有用的实现(但\unless无论如何都更容易):

\documentclass{article}
\usepackage{etoolbox}

\newcommand{\newdoubleboolean}[1]{%
  \newbool{#1}\newbool{not#1}%
  \csappto{#1true}{\setbool{not#1}{false}}%
  \csappto{#1false}{\setbool{not#1}{true}}%
  \setbool{#1}{false}%
}

\newdoubleboolean{tobe}

\begin{document}

\tobetrue

\ifnottobe Not to be\else To be\fi

\iftobe To be\else Not to be\fi

\tobefalse

\ifnottobe Not to be\else To be\fi

\iftobe To be\else Not to be\fi

\end{document}

在此处输入图片描述

答案4

这是另一个想法。

\documentclass{scrartcl}
\usepackage{etoolbox}
\makeatletter
\newcommand\gobblethree[3]{} % poor hackish solution expecting usual escapechar
\newcommand*\newifnot[1]
 {\newif#1%
  \csappto{\expandafter\gobblethree\string#1true}%
    {\cslet{\expandafter\newifnotaux\string#1}\iffalse}%
  \csappto{\expandafter\gobblethree\string#1false}%
    {\cslet{\expandafter\newifnotaux\string#1}\iftrue}%
  \csuse{\expandafter\gobblethree\string#1false}}
\newcommand*\newifnotaux[3]{ifnot}

\newifnot\iftobe
\begin{document}
  \tobetrue
  \ifnottobe Not to be! \fi
  \tobefalse
  \ifnottobe Not to be! \fi
\end{document}

您甚至可以将代码附加到\newif宏中,以便\ifnot自动定义所有内容。

相关内容