在包函数中调用包函数

在包函数中调用包函数

我正在编写一个目前运行正常的包。

我想使用函数

\makeatletter
\def\zz#1{\zzz0#1\relax}
\def\zzz#1#2{%
\ifx\relax#2 \the\numexpr#1\relax
\else
\expandafter\zzz\expandafter{%
  \the\numexpr(#1+\ifnum\expandafter`\string#2<"80 1\else \ifnum\expandafter`\string#2>"BF 1 \else 0 \fi\fi
  \expandafter)\expandafter\relax\expandafter}%
\fi}

在这样的文档中,它可以正常工作

\begin{document}
    \zz{容容}
    \zz{abc}
    \zz{¢Àïα}  
\end{document}

当我想在包函数中使用 zz 函数时,它开始抛出奇怪的错误。我尝试使用 \expandafter,但没有成功。

我正在尝试在这样的函数中调用 xx 函数

\newcommand\hanzibox[2]{%
    \begingroup%
    % Set keys + defaults if options not provided
    \setkeys{hanzibox}{pinyin={},character={},translation={},#1}%
    \setkeys{hanzibox}{inner={star},border=yes,borderWidth={0.4pt},borderColor={0,0,0},#2}%
    \zz{\mm@char}% \mm@char is a correctly set key ----- THIS THROWS ERRORS
    \drawhanzibox%
    \endgroup%
}

答案1

(这个答案实际上是对“功能”的长篇大论,但无论如何可能对你有帮助……)


在包函数中调用包函数 [...] 我想使用函数 [...] 当我想在包的函数中使用 zz 函数 [...] 我试图在函数中调用 xx 函数

TeX 中没有函数。它只有宏,即简单的文本(标记)替换。

这种区别很重要,因为从大多数典型的编程语言中,你可能期望函数以某种方式运行:

  1. 通过查看函数被引用的位置,可以判断它需要多少个参数以及它的“影响范围”(在输入文本中)在哪里结束。例如,在大多数语言中,如果你看到f(a, b); g(c),你就会知道该函数f需要两个参数ab,这g是在之后“调用”的另一个函数f。在 TeX 中,如果你看到\f{a}{b}\g{c},那么一切皆有可能:

    • 可能\f需要零个或一个参数(离开{a}{b}{b}保留在输入流中),
    • 也许只需要两个,
    • 甚至可能需要三到四个(因此 token\g是传递给 的参数之一\f),
    • 也许还需要更多,
    • 也许事情会失败Use of \f does not match its definition
    • 也许\f只需要一个参数{a},但它会扩展为需要多个参数的东西,并且事物将会消耗\g
    • \f在不同情况下可能会扩展到不同的东西,并且在某些情况下会比其他情况消耗更多的令牌。

    更糟糕的是,所有这些模式实际上在 TeX 代码中都很常见,例如在 LaTeX 和包中的宏定义中。(只有 LaTeX 为文档作者提供的外部接口基本一致。)通过查看源代码是无法分辨的\f{a}{b}\g{c}

  2. 在需要值的地方,您可以替换为返回值的函数调用。例如,如果函数调用g(2)返回值21,并且如果f需要一个参数,则写入f(g(2))(某种程度上,模数副作用) 等同于写入f(21)。在 TeX 中,写入\f{\g{2}}将导致\f获得四个标记序列 ( \g, {, 2, }) 作为其#1。(所有这些都是简化,因为如果 catcodes 发生变化,情况可能会有所不同。)

(考虑到所有这些,您可能需要考虑是否真的想用 TeX 编程,以及这是否是您可能长期喜欢的事情……它当然是一个有趣的谜题,但也可能令人沮丧。)


在这种情况下,\zz你得到的解决方案(问题中的)的工作原理如下,类似于\zz{abc}:首先,\zz获取三个标记abc。然后通过巧妙的扩展\zzz反复留在输入流上,以使用(#1#2)对(0,a),(1,b),(2,c),(3,\relax),之后将其扩展为 3。

现在在你的情况下,使用\zz{\mm@char},首先\zz将获得令牌 \mm@char作为参数#1,而不是的值\mm@char。(如果@没有“字母”的 catcode,它将获得五个标记的序列(\mm,,,,,,)@,这甚至更糟。)这将不起作用,因为c期望获取字符,它会对其进行类似和的测试,因此这将不起作用。har\zzz\ifnum \expandafter `\string #2<"80#2\mm@char

换句话说,您遇到了上面提到的第二个问题:与大多数编程语言不同,即使\mm@char定义为扩展为的东西abc,您也不能用 来\zz{\mm@char}代替\zz{abc}

解决此问题的方法取决于\mm@char(您尚未给出)的定义:

  • 如果\mm@char是可以在一个扩展步骤中扩展为字符序列的东西,那么您可以使用\expandafter\zz\expandafter{\mm@char}它,这会导致 TeX 实际上看到\zz{<expansion of \mm@char>}
  • 如果\mm@char分两步扩展为最终序列(即,它扩展为一个宏,该宏又扩展为所需的字符序列),则可以执行以下操作: \expandafter\expandafter\expandafter\zz\expandafter\expandafter\expandafter{\mm@char}这会导致输入流首先被替换为\expandafter\zz\expandafter{<expansion of \mm@char>},然后被替换为\zz{<expansion of expansion of \mm@char>}
  • 如果\mm@char要扩展至其最终序列需要更多步骤,则需要更多\expandafters。
  • 如果\mm@char扩展到多个标记(这不是您想要的),或者涉及进行某些状态改变计算(即,它涉及到达 TeX 的“胃”并由其处理的命令),那么您就必须寻找替代解决方案。

据我所知,没有其他编程语言需要f(g(2))你根据所g采取的步骤数来编写不同的东西。有一种方法可以在函数术语中合理化这一切(例如,说“类型”g在不同情况下是不同的),但最好使用正确的思维模型:标记替换,而不是函数调用。

相关内容