我很难理解(并且欣赏)的概念可扩展性. 我对理解很模糊 什么时候和如何可扩展性影响了我为文档编写代码。
我读了为什么并非所有事物都可扩展?。答案很有趣也很有用,但它没有触及我好奇的核心。我还仔细阅读了其他一些涉及可扩展性的问题的答案:特别有趣的是这个帖子。
回答我最近的一个问题,据解释,文档命令是受保护因此不是可扩展理解了这一点,我就能写出我想写的东西,并得到我期望的效果。
和在对我的另一个问题的评论中,解释了\cs_new_protected:Npn
当函数执行不可扩展的工作(例如设置标记列表或序列)时应该如何使用。
多年来,我一直在写这样的代码
\newcommand{\currentanswer}{}
\newcommand{\setcurrentanswer}[1]{\renewcommand{\currentanswer}{#1}}
知道在调用 之后\setcurrentanswer
,对 的任何调用\currentanswer
都将产生所需的输出。我在这里依赖(不)可扩展性吗?我不太确定;我只知道它能做我想要的事情。然后有时我知道我可以加入 来\protect
获得我想要的结果:但是,我真的不明白为什么我只知道它能完成工作。
最近,我一直在尝试学习一些 LaTeX3:我玩得越多,就越喜欢它。我一直认为 LaTeX 非常强大,但在定义宏和函数的方式上,它突然变得更加强大和透明。但现在,我似乎也遇到了可扩展性问题,而之前我可以轻轻松松地做自己的事情,而对一些细节一无所知。
虽然我在这里问了多个问题,但我怀疑它们确实有相同的答案:因此我不会将它们分成多个帖子。
有人可以花时间解释一下可扩展性的一些细微差别吗?或者,如果不行的话,可以给我提供一个好的参考吗?
我怎么知道我什么时候在和受保护函数/宏?
是受保护和不可扩张一样的东西?
有人能解释一下受保护LaTeX3 中的函数?
最后,除了上述问题的答案之外,为什么最好保护执行不可扩展任务的函数:例如设置标记和序列?(我是很感兴趣理解最后一个问题。
答案1
可扩展命令是指可以在 TeX\edef
或\write
(以及一些其他地方)中“完全”转换为其输出的命令。例如
\def\testa{\testb}
\def\testb{\testc}
\def\testc{d}
\edef\teste{\testa}
\show\teste
会给
> \teste=macro:
->d.
IE所有步骤都已展开,我们只剩下字符。
对于文本来说,这很简单,但是当你涉及到 TeX 基元时,事情就变得更加复杂了,因为有些基元是可扩展的,有些则不是。广义上讲,任何执行赋值的东西都是不可扩展的。所以如果我们有
\def\testa{\testb}
\def\testb{\testc}
\def\testc{\def\ARG{d}}
\def\ARG{}
\edef\teste{\testa}
\show\teste
我们得到
> \teste=macro:
->\def {d}.
请注意,保持\def
不变但\ARG
消失了:它扩展为定义的状态(空)。
e-TeX 允许我们定义受保护宏。这些不会在 内部扩展\edef
,因此
\def\testa{\testb}
\def\testb{\testc}
\def\testc{\def\NOTARG{d}}
\protected\def\NOTARG{}
\edef\teste{\testa}
\show\teste
现在收益
> \teste=macro:
->\def \NOTARG {d}.
这里有一个微妙但重要的观点:\def
一个不可扩展的原始,而\NOTARG
现在则是受保护宏。你可以\NOTARG
使用以下命令判断它是否受到保护\show
:
> \NOTARG=\protected macro:
->.
告诉\protected
我们需要知道的内容。但是,你必须知道不可\def
扩展。
在 LaTeX3 文档中,我们采用了不同的方法,而不是期望人们学习规则:我们记录哪些函数是可扩展的(它们用星号标记)。其他所有内容都受到保护的原因是“部分”扩展是一个真正的问题。如果你这样做
\def\testa{\let\testb\testc}
\edef\testb{\testa}
你得到
! Undefined control sequence.
\testa ->\let \testb
\testc
因为\let
不受影响,\edef
但\testb
未定义。当您查看“真实”文档时,情况会变得更糟,因为问题可能隐藏在许多层之下。
人们在实际的 LaTeX2e 文档中看到的许多问题(例如他们忘记\protect
和遇到麻烦的地方)如果大多数命令受到保护,就会被绕过。一般来说,你会发现不可扩展的 (La)TeX 代码比可扩展的代码多得多,所以 LaTeX3 的立场是这是例外,当然对于文档命令。(排版是不可扩展的,这就是文档中发生的情况。)
这引出了我所说的“羊和山羊”方法:所有 LaTeX3 代码要么是受保护的,要么是完全可扩展的 [“安全”(将给出预期结果)在\edef
/x
类型扩展中],即使我们谈论的是辅助函数。结果是,我们始终可以确定函数是否可以在扩展上下文中使用:如果可以,则用星号标记,否则它将受到保护并且不会部分扩展。因此,编写 LaTeX3 代码的“正确”方法是,如果您使用任何事物不可扩展(IE未在文档中加星号)在你的代码中,那么你有使用\cs_new_protected:Npn
或类似用途,以及不是 \cs_new:Npn
,ETC。
答案2
在
\newcommand{\currentanswer}{}
\newcommand{\setcurrentanswer}[1]{\renewcommand{\currentanswer}{#1}}
您的设置命令不可扩展,它\renewcommand
本身会扩展到其他东西,但最终它会任务(使用\def
)它设置\currentanswer
为传入的标记#1
。但是,宏\currentanswer
被定义为扩展为传入的值,因此只要传入的值是安全的,就可以安全地在扩展上下文中使用。
注意如讨论的 完全可扩展宏的优点和缺点 将命令分类为可扩展或不可扩展其实没有任何意义,任何宏的定义都会扩展为其定义,区别在于上下文,无论你是仅有的进行扩展,或者您是否处于 TeX 的正常操作模式,其中扩展与分配和其他不可扩展的原始操作交错。
可以尝试,但另请参阅上面的链接
你需要查看它的文档或定义。有两种不同的保护机制:LaTeX
\protect
系统和 e-tex\protected\def
系统。*\show\pounds > \pounds=macro: ->\protect \pounds . <*> \show\pounds ? *\protected\edef\foo{abc} *\show\foo > \foo=\protected macro: ->abc. <*> \show\foo ?
上述交互式会话显示
\pounds
的定义为\protect\pounds
(那里的第二个标记在其名称末尾有一个空格)因此依赖于 的 LaTeX 定义\protect
。\foo
使用 e-TeX 保护机制(LaTeX3 命令最常使用的机制)进行定义,并在输出中显示\show
。标记\foo
受 tex 引擎本身保护(因此不会在仅扩展的上下文中扩展),并且不需要内部宏来保存实际定义,就像 的情况一样\pounds
。不可以。不可扩展的原语(比如
\def
在胃中工作)和受保护的命令都是可扩展的宏,但是您需要它们在某些情况下不扩展(例如,它们将辅助文件作为命令名而不是其当前定义写入)受保护的命令通常是可扩展的,但在仅扩展的上下文中充当不可扩展的命令。由于它们带来的意外较少,因此更受青睐。(通常情况下)。
如果在写入或例如
\edef
需要\caption
将文本写入文件的地方使用未受保护的命令,则该命令将会失败.toc
。
答案3
整个可扩展性主题需要或多或少陡峭的学习曲线。
这是一个很长的回答。归根结底,我试图总结一下我应该从哪里开始 LaTeX 编程?。
并且考虑到一些善意,它需要一些工具知识来显示代码的实际结果。
如果你更多地了解所涉及的内部原理,例如如何定义宏,就可以理解可扩展性的概念没有或者和扩大论点。
直截了当地说: 的结果是什么\renewcommand{\currentanswer}{\content}
? 会\currentanswer
扩展为\content
,进而扩展为它“包含”的内容?还是 会在 之后\currentanswer
包含 的“值”的副本?当然,只有在 之后立即发生变化时,差异才是可衡量的。\content
\renewcommand
\content
\renewcommand
虽然这看起来是一个技术区别,但它与您的问题直接相关。
如果在命令后立即\section{\currentanswer}
修改值,会发生什么情况?\currentanswer
\section
另一个问题是:如果你写会发生什么\section{\tikz \draw ... ;}
?显然,这是一个不同的用例:我们放置了一些可执行文件部分参数内的代码。
\def
简短的回答是:您可能需要了解和\edef
(“扩展” )之间的区别\def
。并且您应该知道\section{}
用于\edef
收集其参数;这同样适用于 LaTeX 中的其他“可移动”参数(提供给分段命令、索引、交叉引用等的任何内容)。归结\renewcommand
为\def
;它不会扩展参数。在 的主体内部\edef
,任何条件都将被扩展。任何“可执行”的内容都将不是可以扩展,需要保护。这是一条经验法则;您仍然需要了解“可执行”的含义。但通常,直觉就足够了(例如“包含值的宏不可扩展”,而“\tikz ... ;
当然可以扩展”)。
有趣的辅助实用程序是\show<macroname>
检查\message{The meaning of content is \meaning\content^^J}
您在自己的代码中所写的内容。
此外,在 LaTeX 中,如果您将任何“可执行”参数作为某些分段命令的参数提供,则该参数应受到保护。保护通常意味着\protect\macro
。
我建议阅读我应该从哪里开始 LaTeX 编程?如果您想了解有关这些概念的更多信息。它可能会帮助您理解 LaTeX3 的相关高级概念。
答案4
关于如何编写与可扩展性有关的 expl3 代码的一些补充说明。
回想起那个
如果一个函数能够发挥作用,那么它就被称为“可扩展”如你所愿在仅扩展上下文中。
在那些无法扩展的,那些
- 在仅扩展上下文中不会出错,并且
- 在正常情况下最终执行时做正确的事情
叫做强壮的其余的被称为“脆弱”。
你应该保护不可扩展的函数,否则它可能会很脆弱,而脆弱的命令错误的事情当您意外地/需要将其放在仅扩展的上下文中(例如移动参数)时。
假设你想做类似的事情(类似 C 语法的伪代码)
result = f(g(h(argument)))
其中argument
和result
是变量,f
,g
,h
是函数,应该返回一些结果。
如果所有这些都是可扩展的,那么你可以简单地做
\tl_set:Ne \result { \f:e { \g:e { \h:e { \argument } } } }
但是,如果有些不可扩展怎么办?显然你不能直接在参数中传递它,因为决不供外部宏访问结果。
这里常见的 TeX 习惯用法是该函数将结果存储到某个地方。
在 expl3 代码中,目标通常作为 N 类型参数提供。(与 pgf 相比,数学结果存储在中\pgfmathresult
)
假设f
不可扩展,它通常具有以下形式
\f:Nn <target> <argument>
或者
\f:N <data> % for example regex_replace_all
在这种情况下,您需要“展开”调用链:
\h:Ne \result {\argument}
\g:Ne \result {\result}
\f:Ne \result {\result}