20 l3中宏展开部分对x型参数中双写#的疑问

发布于 2023-08-23 13:29:24

先前雾月大佬在“LaTeX3中控制宏展开的参数说明符x和e有啥区别?”这一问题中说了x型展开和e型展开的区别,基于此我对它们的理解如下:

  • 接收参数完全展开后的内容。所接收的参数中若出现形参符#(类代码为6),必须双写它(即具有##这样的形式);另外,参数说明符中有x的函数本身不会在要被完全展开的环境(x-型展开或e-型展开)中被展开。
  • 接收参数完全展开(递归展开,一展到底)后的内容。与x不同的是:所接收的参数中若出现形参符#(类代码为6),无需双写它;另外,参数说明符中有e的函数在要被完全展开的环境(x-型展开或e-型展开)中可以被展开。

对于在x型参数中双写#这一点:
若使用如下代码(雾月大佬给的例子):

\use:x { \cs_set:Npn \exp_not:N \__foo_do:n ##1 { } }

#的书写满足上述要求,但如果我使用如下代码:

\cs_new:Npx \my_test:n #1 {##1}

则直接报错,#不能双写。

所以,对于x型参数中双写#这一点该如何正确处理,我上面的理解该怎么修正?

查看更多

关注者
0
被浏览
764
雾月
雾月 2023-08-23
这家伙很懒,什么也没写!

并不是所有 x 中的 # 都需要双写。直接用 primitive 实现的一般不需要双写。但是即使这样也有例外情况,这应该是 LaTeX3 的函数命名不一致导致的,由于 \cs_new:Npx 这些函数使用广泛,目前看来不太可能再修改它们的名称了。目前我记得的不需要双写的 #x 参数有 \cs_new...\cs_(g)set... 系列以及 \iow_shipout_x:Nn。所有的 \exp_.. 中的 x 都需要双写(包括 \cs_generate_variant:Nn\prg_generate_conditional_variant:Nnn 生成的变体)。
需要双写的原因是定义了一个临时的宏,见后文。

# 作为引用参数的标识时,它应该仅写一次(随嵌套次数而倍增),当 # 作为它自己时,应该双写。
需要双写的 # 可以使用 \exp_not:n 包裹起来,这样就不要双写。如可以 \use:x { \exp_not:n { \def\tmp#1{#1} } }

\cs_new:Npx 相当于 \long\edef。这里的 x 其实是 e。

定义函数时,\edef\test#1#2{#1,#2},括号里的 #1 #2 是引用第一、二个参数,应该把它们看成一个整体作为 1 个记号,## 也应当看成一个记号。\test 实际上是:两个参数的宏,它展开为 <参数1>,<参数2>
\edef\test#1{\def\noexpand\foo##1#1##2{##1 ##2 #1 \def\foo####1{####1##2}}}\test 是有一个参数的宏,它展开为 \def\foo#1<参数1>#2{#1 #2 <参数1> \def\foo##1{##1#2}}

\use:x 实际上是 \def\use:x#1{\edef\temp{#1}\temp},当写 \use:x{?#1?} 时,\use:x 展开为 \edef\temp{?#1?}\temp,此时在定义 \temp 时就会出错,因为它没有参数,但它的替换文本中却引用了参数,所以必须双写 \use:x{?##1?},此时 ## 表示 #,而不是引用参数的标识。

\use:e 则不是这样,它使用 \expanded primitive 实现,没有定义一个临时的宏,# 直接就是作为它自己。

还是那句话,能使用 e 就使用 e 而非 x

1 个回答

撰写答案

请登录后再发布答案,点击登录

发布
问题

分享
好友

手机
浏览

扫码手机浏览