我最近发布了一个问题,关于如何用纯 TeX 编写宏,该宏将修改用户指定列表中的元素。我随口提到我不喜欢 TeX 的界面,所以有人让我使用 LaTeX3,正如手册所承诺的那样,它应该更像一种现代编程语言。我刚刚开始使用 TeX 编程,因此试图理解手册是一场艰苦的战斗。我猜是因为手册是为经验丰富的 TeX 用户编写的;但是,对于刚开始使用 LaTeX3/TeX 的人来说,似乎没有其他选择,所以我别无选择,只能使用我已有的。这就是我发布此文的原因。手册令人困惑,我想通过问您一些关于语法的简单问题来消除一些困惑。
我应该提一下,告诉我有关 LaTeX3 的人还为我提供了一个使用其界面的原始帖子的解决方案。我能够将此解决方案与手册结合使用,开始找出有关 LaTeX3 语法的一些基本事实。我将谈论我设法弄清楚的内容,但公平地说,其中一些是基于我自己的推论 - 在 stackexchange 用户提供的示例的帮助下得出的 - 而不是手册中的明确说明,所以可能会有错误。我想让你知道,当我有时使用自己的术语时,我并没有混淆问题。只是很难以结构化的方式谈论你不完全理解的主题。
另外,由于篇幅较长,我将这篇文章作为单独的帖子而非评论来写。提前致谢。
--------------------------------------------------------------------------------------------------------------------------
函数定义。
我目前弄清楚了什么:
除其他方法外,还定义了一个新函数,代码如下:
\cs_new_<restrictions>:Npn <function name> <function parameters> {<replacement code>}
是\cs_new_<restrictions>
一个 LaTeX 命令,Npn 用于告诉接口的“解析器”在\cs_new_<restrictions>: Npn
代码部分之后应该期待什么,在本例中,是一个单个标记控制字,即<function name>
,一个或多个参数,即<function parameters>
,以及一个标记列表,即{<code>}
,它替换了函数。
因此,如果我想定义一个接受 4 个参数的新函数,我可以使用以下代码
\cs_new_<restrictions>:Npn \myfunction #1 #2 #3 #4 {<code>}
类似地,具有 2 个参数的函数的代码可能看起来像这样
\cs_new_<restrictions>:Npn \myfunction #1 #2 {<code>}
当然,我假设 - 如果我错了请纠正我 - 空格不是必需的,因为解析器已经被告知如何在“元签名”的帮助下将“元”参数(,,<function name>
)彼此区分开来<parameters>
{<code>}
氮氧化物。
现在,如果我想去掉 #,我可以使用以下通用命令
\cs_new_<restrictions>:Nn <function name>:<function signature> {<code>}
类似的交易,除了现在解析器期望<function signature>
之后出现像 Nn、NnN、TnN 或其他东西<function name>
。
再次,具有 4 个参数的函数可能如下所示
\cs_new_<restrictions>:Nn \myfunction:NNNN {<code>}
还有一个带有 2 个参数的
\cs_new_<restrictions>:Nn \myfunction:NN {<code>}
还有其他命令l3基础用于创建函数的库,但它们的总体结构似乎基本相同。唯一的区别在于它们的功能。例如,使用\cs_set...
而不是\cs_new...
会使函数成为本地函数而不是全局函数。我可能会写一篇后续文章,询问有关 e-type 和 x-type 扩展的更多详细信息,但目前我认为最好还是坚持大局。
不管怎样,到目前为止是这样吗?
好的,继续。
变量定义。
我目前弄清楚了什么:
LaTeX3 中有相当多的数据类型,但主要的有代币列表,字符串,整数,序列, 和逗号分隔列表它们各自使用自己的缩写,但一般来说,定义新变量时,需要声明类型,并在其后跟上关键字,例如新的或者常量取决于您是否初始化变量。
例如,如果我想声明但不初始化一个代币列表变量我使用的代码:
\tl_new:N \mytokenList
然后在某个地方,我可以\mytokenList
使用以下代码存储一个令牌列表:
\tl_set:Nn \mytokenList {<tokens>}
但是,如果我从一开始就知道我想在变量中存储什么数据,我可以使用此命令(不适用于序列或者整数)
\tl_const:Nn \mytokenList {<tokens>}
另外:我注意到,甚至变量都有“函数签名”。这可能使定义解析机制变得更容易。
在我必须指定我所引用的数据类型之前,这大概就是我能做到的最通用的了,因为每个数据类型都有自己相关的操作。
--------------------------------------------------------------------------------------------------------------------------
这就是我目前所拥有的。我很感激任何反馈。这些东西并不容易自学!特别是对 TeX 了解甚少的人,所以如果你们中的一些人看到这个并认为“很明显”,我深表歉意。无论如何,再次感谢。
答案1
定义函数主要有两种方式:
\cs_new<restrictions>:Npn
\cs_new<restrictions>:Nn
其中 可以是_protected
,_nopar
或_protected_nopar
。
两种方法都检查N
后面的参数(即单个标记)是否是当前未定义的控制序列(或活动字符),并且全球定义控制序列。
有什么区别?第一个系列要求,在要定义的控制序列之后,在{
函数的界定“替换文本”之前有一个“参数文本”。
“参数文本”可以是任何标记序列,包括#1
,#2
等等,直到#9
。但是,为了充分领会这种自由的威力,您需要熟悉 TeXbook 第 20 章和“分隔参数”的概念。
不过,我们还是简单点吧。以下两段代码完全等价:
\cs_new:Npn \harry_foo:nn #1 #2 { -#1-#2- }
\cs_new:Nn \harry_foo:nn { -#1-#2- }
因为后者会#1#2
根据要定义的函数的签名自动提供参数文本,在本例中是:nn
。
签名应该由n
和N
字符序列(可能为空)组成。
请注意,当处于活动状态时,空格将被忽略\ExplSyntaxOn
,因此
\cs_new:Npn \harry_foo:nn #1 #2 { -#1-#2- }
\cs_new:Npn \harry_foo:nn #1#2 { -#1-#2- }
\cs_new:Npn \harry_foo:nn #1#2{ -#1-#2- }
都是等效的。即使在 之后也可以有一个空格#
,但我不建议这样做。
TeX 的语法规则规定,当它需要一个“参数文本”时(基本上,在执行\def
或类似的分配时,并存储了要定义的宏的名称之后)一切直到第一个{
都是参数文本的一部分。没有办法预见参数文本是什么,因此特殊的参数p
说明符只表示“直到的一切{
”。
#1
只能自动生成诸如等简单的参数文本#1#2
,这是在使用第二个系列时完成的\cs_new<restrictions>:Nn
。
你错在哪里?假设你可以T
在签名中使用它作为说明符。参数说明符T
或在执行F
时添加。\prg_new_conditional<restrictions>:Nnn
另外,您对参数文本的分析是错误的,如前所示。
那么\cs_set<restrictions>:Npn
和 呢:Nn
? 上面的所有内容都适用,不同之处在于,不会检查要定义的函数是否已定义,并且其含义将被默默覆盖,但声明的范围与当前组一致。 通常,\cs_set...
用于需要适应上下文的临时函数,因此它们的含义不是固定的。
变量的命名约定建议其名称以l
、g
或开头c
。实际上,代码中使用的变量expl3
应符合惯例;可以使用“正常”名称,例如要在文档中使用的\myTokenList
类型变量tl
(也可能是)。clist
以 开头的变量l
应始终在本地起作用(\tl_set:Nn
比如说),而以 开头的变量g
应始终在全局起作用(\tl_gset:Nn
比如说)。
以 开头的变量c
是常量并且应该绝不在被赋予值之后才采取行动,但仅仅是被使用。
可以使用以下方式定义常量
\tl_const:Nn \c_harry_foo_tl {<tokens>}
\str_const:Nn \c_harry_foo_str {<tokens>}
\clist_const:Nn \c_harry_foo_clist {<comma list>}
\seq_const_from_clist:Nn \c_harry_foo_seq {<comma list>}
\prop_const_from_keyval:Nn \c_harry_foo_prop {<key-value list>}
\int_const:Nn \c_harry_foo_int {<integer expression>}
\fp_const:Nn \c_harry_foo_int {<fp expression>}
\bool_const:Nn \c_harry_foo_bool {<boolean expression>}
\dim_const:Nn \c_harry_foo_dim {<dimen expression>}
\skip_const:Nn \c_harry_foo_dim {<skip expression>}
\muskip_const:Nn \c_harry_foo_dim {<muskip expression>}
\intarray_const_from_clist:Nn \c_harry_foo_intarray {<comma list>}
\regex_const:Nn \c_harry_foo_regex {<regex>}
\cc_tab_const:Nn \c_harry_foo_cctab {<code>}