我正在编写一个目前运行正常的包。
我想使用函数
\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 中没有函数。它只有宏,即简单的文本(标记)替换。
这种区别很重要,因为从大多数典型的编程语言中,你可能期望函数以某种方式运行:
通过查看函数被引用的位置,可以判断它需要多少个参数以及它的“影响范围”(在输入文本中)在哪里结束。例如,在大多数语言中,如果你看到
f(a, b); g(c)
,你就会知道该函数f
需要两个参数a
和b
,这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}
。- 可能
在需要值的地方,您可以替换为返回值的函数调用。例如,如果函数调用
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
期望获取字符,它会对其进行类似和的测试,因此这将不起作用。h
a
r
\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
要扩展至其最终序列需要更多步骤,则需要更多\expandafter
s。 - 如果
\mm@char
扩展到多个标记(这不是您想要的),或者涉及进行某些状态改变计算(即,它涉及到达 TeX 的“胃”并由其处理的命令),那么您就必须寻找替代解决方案。
据我所知,没有其他编程语言需要f(g(2))
你根据所g
采取的步骤数来编写不同的东西。有一种方法可以在函数术语中合理化这一切(例如,说“类型”g
在不同情况下是不同的),但最好使用正确的思维模型:标记替换,而不是函数调用。