调试

调试

我尝试创建一个环境,如果 @showtrue 则显示其内容,否则不显示。当我尝试运行下面的 MWE 时,我收到一条错误消息:

! Extra }, or forgotten \endgroup. 
\endToShowOrNotToShow ->\egroup
                                \ignorespacesafterend 
l.24 \end{ToShowOrNotToShow}

我的 MWE:

\documentclass{article}
\makeatletter
\newif\if@show
\def\to@show#1{#1}
\def\not@to@show#1{}
\newenvironment{ToShowOrNotToShow}{%
    \if@show%
        \to@show\bgroup%
            \hfill\\ {\bfseries Behold: }%
    \else%
        \not@to@show\bgroup%
    \fi%
    \ignorespaces%
    }%
    {%
    \egroup%
    \ignorespacesafterend%
    }
\makeatother
\begin{document}
Normal text
\begin{ToShowOrNotToShow}
    Text that is not necessarily shown.
\end{ToShowOrNotToShow}
Rest
\end{document}

我希望它能够扩展为:

Normal text
\not@to@show\bgroup\ignorespaces Text that is not necessarily shown.\egroup\ignorespacesafterend

据我所知,这应该和

Normal text
\not@to@show{Text that is not necessarily shown.}

我的推理的缺陷在哪里?

答案1

(回答“我的推理哪里有缺陷?”部分以便理解……)

调试

首先,让我们调试一下发生了什么。您的 MWE 定义了一个\newenvironment,它根据所\newif调用的执行一件事或另一件事\if@show。在示例中,错误来自“false”路径,因此我们可以专门化 MWE 以仅考虑该部分(并且为了简单起见,还可以省略所有 catcode 和\ignorespaces内容)。因此,您的 MWE 进一步最小化为:

\documentclass{article}
\def\notToShow#1{}
\newenvironment{ToShowOrNotToShow}{%
    \notToShow\bgroup%
}%
{%
    \egroup%
}
\begin{document}
Normal text
\begin{ToShowOrNotToShow}
    Text that is not necessarily shown.
\end{ToShowOrNotToShow}
Rest
\end{document}

这与问题中的错误相同。更进一步,我们实际上可以做我们想象的事情\newenvironment,即在使用环境时将其两个参数放在内部内容之前和之后。(这正是你在问题中所做的,你写了“我希望它扩展为...”。)所以:

\documentclass{article}
\def\notToShow#1{}
\begin{document}
Normal text
\notToShow\bgroup
    Text that is not necessarily shown.
\egroup
Rest
\end{document}

现在错误稍有不同(如果我们感兴趣,我们可以稍后再回过头来研究为什么错误不同),但仍然是一个错误:

! Too many }'s.
l.7 \egroup

错误是什么可能已经很清楚了,但如果不清楚的话,您可以添加:

\tracingonline=1
\tracingmacros=1

(或者只是\tracingall在丰富的输出中搜索)以在 TeX 输出中看到:

\notToShow #1->
#1<-\bgroup 

这显示了发生了什么:宏\notToShow获取标记\bgroup作为其参数(#1),然后(根据其定义)用空值替换该标记。稍后,当\egroup遇到 时,它与任何先前的 都不对应\bgroup(回想一下,它\notToShow消耗了该标记),这解释了错误。

建议

在学习了一点 TeX 和编写宏等知识后,我发现一个似乎有用的技巧。这个技巧就是不是开始写出宏的定义,就像你用另一种语言编写函数一样。相反,回想一下,宏只是文本(或标记)替换(即它们通过扩展工作),然后写出扩展部分第一的。确保它能正常工作。仅然后开始编写宏,纯粹是为了避免每次都输入整个内容而使用的快捷方式。(毕竟,这是在 TeX 中包含该功能的最初目的。)

(实际上,我学到的更重要的教训是,如果可能的话,完全避免编写宏,而是在外部程序或 LuaTeX 回调中进行文本处理……)

在这种情况下,如果你从完全展开的

\toShow\bgroup
    Text that is not necessarily shown.
\egroup

\notToShow\bgroup
    Text that is not necessarily shown.
\egroup

(分别针对ifelse案例),您可能已经遇到了else案例中的问题。(您不会遇到if案例中的问题,因为\def\toShow#1{#1}应用于\bgroup(非预期)只是放回\bgroup,因此\egroup并不意外。)

解决方案

对于宏扩展,不能使用 来调用宏\foo {bar},而不能使用\foo \bgroup bar\egroup。TeX 不是这样工作的;它会查找{具有该 catcode (1) 的任何字符的显式 或 。(事实上,如果它允许在这里使用\bgroup\egroup,那么这将违背它们的目的,您可以直接在任何地方写{}。)

但是\bgroup\egroup适用于许多其他 TeX 原语,并且您可以在此处使用(如评论中所建议)的是\vbox

因此,要编写宏,首先验证您是否拥有所需的内容,然后完全展开:

\documentclass{article}
\begin{document}

% To show the text, just insert it into a vbox.
Normal text
\vbox\bgroup
    Text that is not necessarily shown.
\egroup
Rest

% To hide the text, assign to box 0 and don't use it.
Normal text
\setbox0\vbox\bgroup
    Text that is not necessarily shown.
\egroup
Rest

\end{document}

输出

(实际上这看起来并不理想,但它或多或少与您在另一个答案中所说的“这可以完成我想要它做的事情”相符。\ignorespaces为了简单起见,我删除了“Behold”等,但想象一下你在这里有适当的东西。)

接下来介绍一下\if

\documentclass{article}
\begin{document}

\newif\ifshow
\showtrue % Try with and without this

Normal text
\ifshow
  \vbox\bgroup
\else
  \setbox0\vbox\bgroup
\fi
    Text that is not necessarily shown.
\egroup
Rest

\end{document}

最后,只有当您满意时,才定义您的环境:

\documentclass{article}

\newif\ifshow

\newenvironment{ToShowOrNotToShow}{%
\ifshow
  \vbox\bgroup
\else
  \setbox0\vbox\bgroup
\fi
}{%
  \egroup
}

\begin{document}

\showtrue % Try with and without this

Normal text
\begin{ToShowOrNotToShow}
    Text that is not necessarily shown.
\end{ToShowOrNotToShow}
Rest

\end{document}

您可以在此处添加\ignorespacesafterend特定于 LaTeX 环境的其他内容。

答案2

根据马丁·沙雷尔我制作了下面的示例。我分步创建和打印框。此外,我不再需要\not@to@show\to@show

\documentclass{article}
\makeatletter
\newif\if@show
\newenvironment{ToShowOrNotToShow}{%
\if@show%
    \vbox\bgroup%
        \hfill\\ {\bfseries Behold: }%
\else%
    \setbox0\vbox\bgroup%
\fi%
\ignorespaces%
}%
{%
\egroup%
\ignorespacesafterend%
}
\makeatother
\begin{document}
Normal text
\begin{ToShowOrNotToShow}
    Text that is not necessarily shown.
\end{ToShowOrNotToShow}
Rest
\end{document}

这就是我想要它做的事情。

相关内容