LaTeX3中控制宏展开的参数说明符中有x型和e型,都表示完全展开,请问两者具体的区别是啥?比如下面这两个函数:
\use:x{<可展开的记号>}
\use:n{<可展开的记号>}二者的区别在何处?
这两个宏都需要吃掉一个参数,并且完全展开这个参数。
第一个区别是,\use:e(在要被完全展开的上下文中)可以被完全展开(expands all tokens fully),\use:x 不能被完全展开。
比如 \use:e { <tl> } 中 <tl> 就是“要被完全展开的上下文”,如果这里面有 \use:e,它也会被完全展开,\use:x 则不会被展开。
例如,
% protected 宏 \__my_unexp: 在要被完全展开的上下文中不会被展开,保持原样
\cs_set_protected:Npn \__my_unexp: { do~something }
\tl_set:Nn \l__my_tl { do~other }
\use:e { \__my_unexp: \use:x { \l__my_tl } }其结果是 \__my_unexp: \use:x { do~other },\__my_unexp: 和 \use:x 不会被展开,括号也不会被展开,但是 \l__my_tl 会被展开,这个展开是由 \use:e 引起的,而不是 \use:x。
而
\use:e { \__my_unexp: \use:e { \l__my_tl } }的结果是 \__my_unexp: do~other,\__my_unexp: 不会被展开,但是里面的 \use:e 会被展开,它需要一个参数,即 \l__my_tl,然后这个里面的 \use:e 展开它的参数 \l__my_tl。
有没有办法阻止记号在要被完全展开的上下文中被展开呢?有。就是 \exp_not:N 和 \exp_not:n(及其变体)。
\use:e { \__my_unexp: \exp_not:N \use:e { \l__my_tl } }这样,里面的那个 \use:e 就不会被展开,但 \l__my_tl 会被外面那个 \use:e 展开,结果是 \__my_unexp: \use:e { no~other }。
而
\use:e { \__my_unexp: \exp_not:n { \use:e { \l__my_tl } } }结果是 \__my_unexp: \use:e { \l__my_tl },因为 \exp_not:n 的作用是让它的参数不被展开。
以上代码把外面的那个 \use:e 换成 \use:x,结果完全相同。
另一个区别是,\use:x 的参数中,parameter(catcode=6),比如 #,需要双写,但 \use:e 不需要,如:
\use:x { \cs_set:Npn \exp_not:N \__foo_do:n #1 { } }
\use:e { \cs_set:Npn \exp_not:N \__foo_do:n #1 { } }第一行会报错。需要写成 ##1。
除此之外,它们的作用完全相同。
l3kernel 昨天(2023-05-17,版本为 Released 2023-05-15)的更新中,要求引擎必须有 \expanded primitive 了(从 TeXLive 2019 开始 pdfTeX、XeTeX、LuaTeX 都已经有这个 primitive 了),\use:e 也是使用 \expanded 实现的。
目前,有 e 变体的应当用这个变体,使用 \cs_generate_variant:Nn 时,也应生成 e 变体。
某些宏和 primitive,比如 \tl_set:Nx、\cs_set_nopar:Npx,它们内部使用的 primitive 已经完成了完全展开这个操作,所以不再需要使用 e 变体来展开。(当然不全是如此)
TeX(和 LaTeX)里关于“展开”的内容可以写一篇10页以上的文章了。
非常感谢!大佬太强了,简直就是一本行走的LaTeX3百科全书!
不好意思,上面那个
\use:n应当是\use:e,我写错了,不过雾月大佬已经猜到了我要说啥。@u10307 大佬,关于x型参数中双写#的问题,我还有疑问,假设我用具有x型展开的定义类函数定义一个函数,如下:
这时若将x所对应的参数中的#双写:
\cs_new:Npx \my_test:n #1 {##1},反而报错,这是为啥?x所对应的参数中#双写到底该怎么理解?@u10508
\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 ####1}},\test是有一个参数的宏,它展开为\def\foo#1<参数1>#2{#1 #2 <参数1> ##1}。当
#作为引用参数的标识时,它应该仅写一次(随嵌套次数而倍增),当#作为它自己时,应该双写。而
\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则不是这样,它使用\expandedprimitive 实现,没有定义一个临时的宏,#直接就是作为它自己。好的好的,非常感谢,这也太贴心了,大佬又来这回答了一次。