LaTeX3 中引入的编程语言expl3
与其他 TeX 宏语言相比有点不寻常,因为它同时使用 _ 和 : 作为函数名称。有人能提供一些关于语法优缺点的技术见解吗?
答案1
免责声明:最初这个问题的表述是喜欢/不喜欢,而不是优点/缺点,这是我唯一能给出答案的方式。我在 expl3 方面没有足够的实践经验来理解该语言的技术(缺点)。所以这个答案只是一个(外部)意见。
对于了解我作为 luatex 和 ConTeXt 开发人员的背景的读者来说,我不喜欢新的语法应该并不奇怪。
我的主要反对意见是它给本来就非常复杂的任务(宏编程)增加了额外的复杂性。
以的定义\mode_if_horizontal:TF
为例:
\prg_set_conditional:Npnn \mode_if_horizontal: {p,TF,T,F}{
\if_mode_horizontal:
\prg_return_true: \else: \prg_return_false: \fi:
}
看来我必须学习一种全新的编程语言,这种语言与 TeX 宏很相似,但显然并不相同。考虑到我花了一年多时间在一份全职工作上才学会如何正确编写 TeX 宏,这让我感到很害怕。
在示例中,内部宏的命名和逗号分隔列表的使用表明其意图是使宏的行为更像其他语言中的函数。但是,作为一名初学者,在已经对底层引擎有了充分了解的情况下,我并不真正相信新语法能够成功屏蔽底层引擎。问题是,如果它做不到这一点,那么它就不是一种替代语言,而是一种需要在现有宏语言之上学习和使用的附加语言。而且,至少对我来说,它是一种附加语言,具有非常不熟悉的语法和语义。
总而言之,对于更复杂的任务,我更喜欢使用传统的 TeX 风格的宏和 lua。
答案2
(注意:答案涵盖了问题各个版本中的内容!)
这个问题主要涉及两个问题,即“重命名原语”的想法和“参数规范”。作为背景,环境expl3
将_
和:
“字母”包含在控制序列中,并且还会忽略空格。至少在 LaTeX2e 中,使用作为“字母”有些不系统。使用和@
的想法是它们的角色更加明确,用于将函数和变量名称划分为可读的“块”,并用于显示函数所采用的参数。_
:
_
:
关于“重命名原语”,主要直接使用原语不是语言的想法expl3
。在问题中,情况
\ifXXX:Nno
...
\fi:
给出了。为了更具体一点,当前的 expl3
将所有原始 TeX\if...
语句重命名为,例如
\if_mode_horizontal: % \ifhmode
....
\else:
...
\fi:
然而,这个想法是,在大多数情况下(内核本身之外),不应该真正使用这些原语。因此,建议的测试是
\mode_if_horizontal:TF { true-code } { false-code }
两个分支或
\mode_if_horizontal:T { true-code }
\mode_if_horizontal:F { false-code }
对于单个分支。我认为文档甚至不应该提及“重命名的原语”,因为有更好的替代方案。
关于“参数指定”的想法,我首先应该说一下它是什么。expl3
命名函数的方法的想法是_
使用字母和来生成名称,然后:
使用字母来结束名称并开始参数列表。这可以以几种方式使用。首先,我上面展示了我们如何拥有三个相关的函数
\mode_if_horizontal:T
\mode_if_horizontal:F
\mode_if_horizontal:TF
其中姓名是一样的,但是参数是不同的。所以我的想法是,我可以阅读上面的内容并快速了解测试应该实现的目标。
“参数规范”思想的第二部分是它允许以系统的方式显示扩展。一个例子是\cs_set_nopar:Npn
,它是expl3
的名称\def
。在 LaTeX2e 中,我们也有 的\@namedef
缩写形式
\expandafter\def\csname #1\endcsname
但很难将其扩展到所有可能性。 中相同的想法expl3
称为\cs_set_nopar:cpn
。请注意,唯一的区别是参数规范:
\cs_set_nopar:Npn \ControlSequence #1#2 { stuff here }
\cs_set_nopar:cpn { ControlSequence } #1#2 { stuff here }
然后我可以进一步操作,如上\edef
所示,在最后一个位置使用“x”而不是“n”:
\cs_set_nopar:Npx \ControlSequence #1#2 { stuff here } % \edef
\cs_set_nopar:cpx { ControlSequence } #1#2 { stuff here } % `\@nameedef`
显然,可以提供更多的例子,但最好在“expl3.pdf”或我最近撰写的 TUGBoat 文章中研究。
问题是社区是否喜欢这些想法。对此,除了个人观点外,我无法提供真正的答案。当然,语法有一个学习曲线,这会让一些人望而却步。但是,我认为它确实解决了至少一些用 TeX 编写代码的问题。
问题还询问了缺点。一个明显的问题是_
TeX 使用 来表示下标,因此使用 来编码下标时会出现一些问题expl3
。可以使用正常的 TeX 方法生成带有难懂类别代码的标记来解决此问题:(至少\lowercase
在 中expl3
\tl_to_lowercase:n
,目前如此)。显然,这需要一些有关如何编程 TeX 的知识,如果有替代方案就更好了。
还有一点是,随着新事物的出现,它仍会发生变化。虽然这意味着可能会发生有用的变化,但也意味着您需要做好准备以保持最新状态。通常,更改会在 CTAN 上传中标记出来,但如果您想确保一切正常,expl3
仍然需要对当前版本进行测试。expl3
(LuaTeX 提供了使用 Lua 进行编程的可能性,这显然是的另一种方法expl3
。这似乎是一个很大的区别,我不会试图在这个答案中涵盖它!)
答案3
空格
编写 expl3 代码时,空格会被忽略。这样可以生成更易读的代码,因为大多数行的末尾不会出现注释字符。考虑
\cs_set:Nn \foo: {
\mode_if_math:TF {
\foo_math:
}{
\foo_text:
}
}
现在:
\cs_set:Nn \foo: {%
\mode_if_math:TF {%
\foo_math:
}{%
\foo_text:
}%
}
这看起来可能不多,但它减轻了处理空格问题的认知负担。您可以更改缩进/括号样式,而不必担心是否需要添加%
,并且可以使用更多类似 C 的括号语法,而不会到处乱七八糟%
。
顺便说一句,如果您需要在 expl3 代码中插入空格,您可以使用 ~ 字符,其 catcode 为 10。
答案4
(回答修改后的问题。)
TeX 不提供任何命名空间,因此只有惯例将一个包的宏/变量与另一个包分开。Knuth 提出了在纯 TeX 格式中使用额外“字母”的想法,他使用的@
名称不用于排版。LaTeX 和 ConTeXt 都继续使用一个或多个额外“字母”的想法来表示内部宏。从技术层面上讲,没有理由强制进行这种分离:只需使用大小写变体来使宏的来源显而易见即可。例如,对于包“foo”,通常的 LaTeX2e 方法是使用
\foo@internal@macro
而expl3
我们可能
\foo_internal_macro:
但没有理由不简单地使用
\FooInternalMacro
从纯技术角度来看。
LaTeX2e 使用@
作为“字母”。遗憾的是,这并不是系统地完成的,内部名称通常包含任意数量的@
符号。这不会使代码清晰。但是,这与符号的选择无关:@
可以系统地应用。ConTeXt 在代码块中使用了更多的“字母”,例如!
,并且更加系统化。
选择_
和:
在expl3
某种程度上是任意的,尽管这两个符号在其他编程语言中都可以看到。据我所知,@
很少以这种方式使用。由于 TeX 仅依赖于类别代码,因此_
和的选择:
可以被视为可读性问题。这当然是一个非常主观的领域!
使用的一个明显问题_
是 TeX 使用它来表示下标。代码(用于expl3
)和排版数学(用于下标)之间的重叠很小。这意味着当代码块中需要 a 来表示下标时,可以_
使用原语的方法。但是,您可以争辩说,这里的另一种选择可能更好。\lowercase
_