编辑
感谢@PhelypeOleinik 指出问题不在于它\str_foldcase
本身,而在于 switch 语句主体在文档开始之前试图排版文本。尝试学习导致认知负担过expl3
重,以至于我缺乏发现这种基本错误的能力。
新MWE
这有效,但不会打印所选语言的值,也不会真正选择语言。我不确定变量的命名\l__myclass_lang_tl
是否是“最佳实践”,因为这是我第一次这样做。欢迎对此提出建议、更正和改进。
据我目前所了解,尽管我可能在多个地方是错误的或不完整的:
`\l` means declare a local variable
`_` (the first one) is just a separator
`_` (the second one) is currently a mystery to me
`myclass` is referencing the name space of the class I'm creating
`_` is just a separator for readability because we're using snake_case
`lang` is the meaningful part of variable name
`_` another separator
`tl` I don't fully understand yet: I know it stands for "token list"
but I'm not sure why this has to be a token list.
I was expecting "str" for "string"
\documentclass[]{article}
\usepackage{xparse}
\ExplSyntaxOn
\tl_new:N \l__myclass_lang_tl
\NewDocumentCommand \selectLang { m }
{
\str_case_e:nnF { \str_foldcase:e { #1 } }
{
{ english } { \tl_set:Nn \l__myclass_lang_tl {english} }
{ norsk } { \tl_set:Nn \l__myclass_lang_tl {norsk} }
{ nynorsk } { \tl_set:Nn \l__myclass_lang_tl {nynorsk} }
{ samisk } { \tl_set:Nn \l__myclass_lang_tl {samisk} }
{ samin } { \tl_set:Nn \l__myclass_lang_tl {samin} }
}
{\tl_set:Nn \l__myclass_lang_tl {error}}
}
\cs_generate_variant:Nn \str_foldcase:n { e }
\ExplSyntaxOff
\selectLang{Samin}
\renewcommand\selectlanguage{} % just to show that it works
\begin{document}
Chosen language: \selectLang{EnGlIsH}
\def\foo{NOrsK}
Chosen language: \selectLang{\foo}
\end{document}
原创(供后人参考)
我相信我遇到了\str_foldcase
需要的麻烦,active char
但我不确定。
我正在编写我的第一个class
文件并希望利用expl3
。我才刚刚开始expl3
,还处于“困惑的初学者”阶段。我感到非常沮丧,因为我无法自己解决这个问题。
对于可以采用参数的类选项,例如\documentclass[fruit=banana]{fruity}
(其他有效选项是apple
和cherry
),我想以不区分大小写的方式处理这些选项,并发现这个看起来很有希望的例子它使用不区分大小写的 switch 语句来创建\selectLang
命令。
但是,当我尝试调用\selectLang
它在前导码(第 18 行)中创建的命令时,这会导致错误no begin document
。
我已经尽我所能研究了这个问题,但似乎active char
我目前还不明白。我见过这但我不明白答案,而且由于它是 2015 年的,并且谈论的是一种新机制,所以它似乎已经过时了。这看起来更现代,但我仍然不明白。
我看过expl3.pdf
xparse.pdf
和,interface3.pdf
但我还太新,无法expl3
真正理解它们。据我所知,没有找到任何示例来说明如何使用的任何变体,\str_foldcase
以便它在序言中发挥作用。
平均能量损失
这是我对原始问题的修改版本。我知道这不是,class
我只是试图让不区分大小写的字符串比较在序言中起作用。我对原始示例所做的唯一两个更改是:首先删除宏调用,因为这会产生一个可以理解的错误,因为您无法在那个阶段选择语言;其次添加第 18 行,该行仍在序言中\selectlanguage
调用。\selectLang
\selectLang
无论序言中的调用是在之前还是之后,我都会得到同样的错误\ExplSyntaxOff
删除\str_foldcase:e
相应的括号意味着没有开始文档错误,这让我认为问题出在\str_foldcase
运行不当。从文档内容运行时,它工作正常。
\documentclass[]{article}
\usepackage{xparse}
\ExplSyntaxOn
\NewDocumentCommand \selectLang { m }
{
\str_case_e:nn { \str_foldcase:e { #1 } }
{
{ english } { selectlanguage~british }
{ norsk } { selectlanguage~norsk }
{ nynorsk } { selectlanguage~nynorsk }
{ samisk } { selectlanguage~samisk }
{ samin } { selectlanguage~samin }
}
}
\cs_generate_variant:Nn \str_foldcase:n { e }
\ExplSyntaxOff
\selectLang{Samin} % Causes an error about a missing begin document
\renewcommand\selectlanguage{} % just to show that it works
\begin{document}
Chosen language: \selectLang{EnGlIsH}
\def\foo{NOrsK}
Chosen language: \selectLang{\foo}
\end{document}
生成
! LaTeX Error: Missing \begin{document}.
See the LaTeX manual or LaTeX Companion for explanation.
Type H <return> for immediate help.
...
l.18 \selectLang{Samin}
You're in trouble here. Try typing <return> to proceed.
答案1
添加原始问题的答案以供参考:您的使用\str_case_e:nn
尝试排版selectlanguage <name>
,因此您无法在类文件中使用它(在之前\begin{document}
)。现在来讨论编辑后的问题。
您的\selectLang
命令只是“解析”用户输入,并\l__myclass_lang_tl
根据该输入设置标记列表以包含一个值。它 \selectLang
不执行任何其他操作(也不打印任何内容),这正是您所看到的。如果您想\selectLang
在使用它时排版某些内容,您必须明确告诉它:
\NewDocumentCommand \selectLang { m }
{
\str_case_e:nnF { \str_foldcase:e { #1 } }
{ ... cases ... }
{ ... }
Selected~language~is~\tl_use:N \l__myclass_lang_tl % <--- This line “uses” the token list
}
然后当你使用它时,它将排版文本Selected language is <value of \l__myclass_lang_tl>
。然而,作为一个好的编程原则,建议将函数分开:一个函数解析用户输入并设置\l__myclass_lang_tl
,另一个函数在你需要时打印文本,因此类似于:
\NewDocumentCommand \selectLang { m }
{
\str_case_e:nnF { \str_foldcase:e { #1 } }
{ ... cases ... }
{ ... }
}
\NewDocumentCommand \printLang { }
{ Selected~language~is~\tl_use:N \l__myclass_lang_tl }
然后设置和打印独立进行。
关于变量和函数命名expl3
你expl3
可以同时拥有公共和私有函数和变量。公共函数和变量应该有文档记录(最好是稳定的),并且可以被其他人使用,而私有函数和变量不应该有公共文档记录,并且你“被允许” 1随意更改或删除它们,只要公共接口不受影响。
命名方案可让您快速识别什么是函数、什么是变量,并轻松区分公共函数和私有函数。函数以 开头,\module_...
当为公共函数时,或\__module...
当为私有函数时。总体规则是:
\(__)<module>_<name>:<args>
% for example:
% \myclass_print_authors: or \__myclass_parse_list:nn
其中__
表示它是一个私有函数,<module>
是字首用于您的类、包或expl3
模块,<name>
是函数的描述性名称,由一个或多个以 分隔的部分组成_
,<args>
是函数的(可能为空)参数签名。
变量可以与函数区分开来,因为它们都以 开头<scope>
:
\<scope>_(_)<module>_<name>_<type>
% for example:
% \g_myclass_title_tl or \l__myclass_author_count_int
其中<scope>
是l
ocal、g
lobal 或c
onstant,extra_
表示私有变量,<module>
和<name>
如上所述用于函数,<type>
是变量的类型(tl
、int
、str
、fp
等)。
[1] 我们讨论的是 TeX,因此实际上你可以重新定义任何东西,只要你愿意接受风险,但关于“这个错误无法修复,因为另一个包依赖于错误的行为”的众所周知的问题使得必须就哪些宏可以依赖以及哪些宏可能会在没有警告的情况下发生变化达成一致。
在tl
vs.str
TeX 没有像通常的编程语言那样的“字符串”概念。TeX 中的所有内容最终都会成为标记(除了少数例外),因此您经常需要存储此类标记,这就是使用标记列表的原因。借助 ε-TeX,\detokenize
您可以强制列表中的所有标记都具有 catcode 12 (other) 2,这几乎是无操作(它们没有特殊含义),因此此类列表的行为或多或少类似于字符串,因此它们获得了特殊名称“字符串”(但仍然是标记列表)。
[2] 空格除外,其类别代码为 10(空格)。
最后
\str_case_e:nn
(和其他\<thing>_case:nn(TF)
功能)可扩展(标有☆或★interface3
),因此您可以使用它来减少代码的重复性:
\NewDocumentCommand \selectLang { m }
{
\tl_set:Nx \l__myclass_lang_tl
{
\str_case_e:nnF { \str_foldcase:e {#1} }
{
{ english } { english }
{ norsk } { norsk }
{ nynorsk } { nynorsk }
{ samisk } { samisk }
{ samin } { samin }
}
{ error }
}
}
通过使用\tl_set:Nx
而不是 ,\tl_set:Nn
您可以在实际将其分配给 之前强制x
对标记列表进行详尽扩展\l__myclass_lang_tl
,并且由于函数\str_case_e:nnF
和\str_foldcase:e
都是可扩展的,因此在x
-扩展时两者都可以工作,因此最终标记列表将包含列出的语言之一或error
。