我是一名货物崇拜程序员……

我是一名货物崇拜程序员……

...因为我不知道为什么这是有效的

\def\my@addextraoption#1{%
    \xdef\my@extraoptions{%
        \@ifundefined{my@extraoptions}\@empty\my@extraoptions,%
         \zap@space#1 \@empty}}
\DeclareOption*{\my@addextraoption\CurrentOption}

但这不行:

\DeclareOption*{%
    \xdef\my@extraoptions{%
        \@ifundefined{my@extraoptions}\@empty\my@extraoptions,%
         \zap@space\CurrentOption \@empty}}

后者给了我这个错误:

! Argument of \zap@space has an extra }.
<inserted text> 
                \par 
l.110 \ProcessOptions\relax

那么这到底是怎么回事呢?当我收到那条难以捉摸的消息(或者来自 TeX/LaTeX 核心深处的其他难以捉摸的消息)时,我倾向于启用我的货物崇拜程序员位Argument of \@foo has an extra }.。最终,我找到了一些似乎有效的语法,然后我对其进行了彻底的测试,以确保它确实有效。

一定有比激发我的“货物崇拜”程序员精神更好的方法。TeX/LaTeX 是极少数能激发我这种精神的语言之一。在这方面,TeX/LaTeX 与 JCL(是的,这确实让我过时了,不是吗)和 postscript/forth 并驾齐驱。

答案1

这与 TeX 何时吞掉空格有关。“何时”实际上有两个部分。首先要知道的是 TeX 在读取输入时会吞掉空格。其次要知道的是 TeX 在命令名称后会吞掉空格。将这两个结合起来,我们看到 TeX 在命令名称后面会吞掉空格在输入中不是当已读代码重新插入到流中时。现在让我们检查一下这两种情况:

  1. \zap@space#1 \@empty 当 TeX 读取这段代码时,它并不知道这#1将是一个命令。据它所知,这#1可能是一堆恶臭的澳洲野狗的肾脏。所以它不会占用空间。因此,当执行此代码时,TeX 会看到:\zap@space\CurrentOption \@empty 仍有空间。因此,每个人都很高兴。

  2. \zap@space\CurrentOption \@empty 当 TeX 读取这个时,它知道\CurrentOption是一个命令,所以它会占用空间。因此处理器看到的实际代码是\zap@space\CurrentOption\@empty,并且没有空间可以删除,所以\zap@space会报错。

可能有很多解决方案。一个简单的方法是确保 TeX 看到该空格时不会处于“空间吞噬”模式。要做到这一点,我们必须想办法让 TeX 在空格之前看到的最后一个东西不是命令。一种常见的做法是将一个空组放在那里:\zap@space\CurrentOption{} \@emptyworks。

编辑:) 正如 Philippe 指出的,上面的代码在 的参数中添加了括号\my@extraoptions。下面这个代码没有添加括号,并且 更少\expandafters

\expandafter\zap@space\expandafter\CurrentOption\space\@empty

但事实上,看看 then 的输出,\my@extraoptions我认为你的代码有问题\@ifundefined。以下是我思考你正在寻找:

\ProvidesPackage{test}

\DeclareOption*{%
  \edef\@temp{%
    \xdef\noexpand\my@extraoptions{%
    \@ifundefined{my@extraoptions}{}{\my@extraoptions,}%
    \noexpand\zap@space\CurrentOption\space\noexpand\@empty}}
  \@temp
}

\ProcessOptions

\show\my@extraoptions

使用调用程序:

\documentclass{article}
\usepackage[hello,world]{test}

\begin{document}
hello
\end{document}

在日志文件中,我得到的\my@extraoptionshello,world。使用您的原始代码,我得到的是,hello,world- 即,带有一个开头的逗号。

除了更正\@ifundefined,我还通过扩展采取了另一种方法。我没有使用过多的\expandafter,而是使用\edef来定义一个临时宏,该宏会根据需要扩展内容(使用 来抑制一些扩展\noexpand),然后直接调用该宏来执行实际定义。

(这是我在这里学到的一个技巧......何时使用 \edef、\noexpand 和 \expandafter?... 看到一个大约 20\expandafter秒的宏被压缩为一个。这也是被指控为“货物崇拜程序员”!)

答案2

的定义\zap@space

\def\zap@space#1 #2{%
  #1%
  \ifx#2\@empty\else\expandafter\zap@space\fi
  #2}

这意味着 会\zap@space逐个删除空格,但需要以空格结尾,后跟\@empty。当您编写时\zap@space#1 \@empty,情况就是这样,因为#1被其内容替换。但是当您编写 时\zap@space\CurrentOption \@empty,TeX 会吃掉后面的空格\CurrentOption(就像它总是对多字母宏后面的空格所做的那样),所以它相当于 ,\zap@space\CurrentOption\@empty这是不正确的,因为 之前没有空格\@empty

如果没有完整的代码示例,我无法确定,但我认为这对您来说会有效:

\expandafter\expandafter\expandafter\zap@space\expandafter\@iden\expandafter{\CurrentOption} \@empty

\@iden扩展后会给出其参数(定义是\def\@iden#1{#1}),因此\@iden{\CurrentOption}与相同\CurrentOption,但由于结尾括号,其后的空格会被保留。但由于需要扩展 \@iden,因此必须使用。为了修剪 的空格,您还需要\expandafter在之前添加两个空格(如果省略它们,则不会出现错误,但不会删除任何空格)。\CurrentOption\expandafter

在看到 Andrew 的回答后补充道:与 Andrew 建议的 () 相比,这样做的好处\zap@space\CurrentOption{} \@empty是结果中没有添加任何虚假的括号。

答案3

这太长了,不能作为评论,并且太旧了,不值得更新我的问题。

三年后,我学到了很多东西\expandafter,甚至可能知道为什么我必须这样做\expandafter\expandafter\expandafter ... \foo \expandafter ...

我认为我使用 TeX / LaTeX 进行货物崇拜编程的经历可以用两个词来概括:漏洞抽象。打个比方,我在 Mac 上使用 macports、fink 和 homebrew。我最喜欢哪个系统?当然不是我现在使用的系统。它是我没有使用的两个系统之一。这三个工具都“省略了诽谤性词语”。

关于 TeX/LaTeX:这些诽谤性的话绝对适用。20 世纪 60 年代基于宏的系统中存在太多漏洞抽象。正是这些宏和漏洞抽象触发了我的货物崇拜编程。

不过,相比其他工具(例如 microstuff 的东西),我还是更喜欢用 TeX/LaTeX 来写文档。至于编写作者需要的底层工具,那是另一个问题。

相关内容