它做什么\csnumgdef
?从代码示例来看,它似乎需要两个参数,并且让人相信它定义了一个数字var
并为其分配了值val
,如\csnumgdef{var}{val}
。如果var
被用作一个简单的计数器,那么这种语法与以下语法相比有哪些优点/缺点:
\newcounter{var}
\setcounter{var}{val}
最后, 的词源是什么csnumgdef
,特别是cs
和g
?我注意到其他命令似乎属于这个cs
家族,例如\csname
、\csuse
和\csdef
,但我不清楚是什么将它们联系在一起。
答案1
\csnumgdef
,来自etoolbox
包(对您来说,由 加载biblatex
)的 定义为:
\newrobustcmd*{\csnumgdef}[1]{%
\expandafter\numgdef\csname#1\endcsname}
其\numgdef
定义为:
\newrobustcmd*{\numgdef}[2]{%
\ifundef#1{\let#1\z@}{}%
\xdef#1{\the\numexpr#2}}
让我们一步一步地看一个例子。假设你做了\csnumgdef{foo}{1+1}
。首先,\csnumgdef
接受一个参数,foo
然后执行({1+1}
上一步的 仍然存在,未发生任何变化):
\expandafter\numgdef\csname foo\endcsname{1+1}
跳过并扩展。构建一个控制序列(因此名称中的) \expandafter
,其中包含匹配的,即参数。这变成:\numgdef
\csname
\csname
cs
\csnumgdef
\endcsname
foo
\numgdef\foo{1+1}
因此你可以得出结论\csnumgdef{foo}
和\numgdef\foo
是等价的。
现在\numgdef
展开,抓住两个参数,\foo
和1+1
:
\ifundef\foo{\let\foo\z@}{}%
\xdef\foo{\the\numexpr 1+1}
测试\ifundef
检查是否\foo
未定义,如果\let\foo\z@
是则执行操作,否则不执行任何操作。
现在来看看好的部分:\xdef
相当于\global\edef
(现在您看到g
in代表),它将进行全局分配,并将扩展定义内的所有内容。它将扩展(cs)numgdef
\global
\edef
\the
,这将触发\numexpr
。\numexpr
然后将评估其后的整数表达式(1+1
)并将结果保留在那里。扩展后,您将得到(某种程度上):
\global\def\foo{2}
现在一切照常,并且现在\foo
扩展到2
。
简而言之,\csnumgdef{<name>}{<intexpr>}
将计算整数表达式<intexpr>
并将其存储在中\<name>
。名称中不带的版本cs
将控制序列作为参数(而不是控制序列名称):,\numgdef\<name>{<intexpr>}
而没有的版本g
执行本地赋值。
这些命名约定很有用(尤其是正确遵循时 ;-),因为您不需要查找某个命令的定义来了解它的作用。如果您知道它的作用,那么推断出它的作用\numdef\foo{1+1}
并不难(当然,前提是您知道这些约定)。\csnumdef
\numgdef
expl3
的约定定义一个函数,set
其名称中包含 ,则执行赋值(与def
in非常相似\numdef
)。如果函数是 ,gset
则这种赋值是全局的。知道这一点,很容易猜出\int_set:Nn
做什么:它(几乎1)与 相同\numdef
:-)
如果您知道这一点,那么\int_gset:Nn
就很容易了。
但是你会问,版本呢cs
?这个是在参数规范中表示的。N
参数不是单个常规参数,而是c
sname。\int_set:cn
(equivalent of \csnumdef
) 和\int_gset:cn
(equivalent of \csnumgdef
) 就是这样做的 :-)
1 \numdef
将数字存储在宏中,而\int_set:Nn
将其存储在计数寄存器中。
答案2
\csnumgdef
定义在etoolbox
包裹。该软件包“是一个编程工具箱,主要面向 LaTeX 类和软件包作者。它为 e-TeX 提供的一些新原语提供了 LaTeX 前端,以及一些与 e-TeX 无关但符合该软件包配置的通用工具。”(摘自etoolbox
文档。
、cs
和命名g
法def
在整个文档中使用,大致定义为
cs
=<csname>
g
=g
全局def
=\def
另一种解释是gdef
类似于\gdef
或\global\def
(或可能\xdef
——x
的扩展版本\gdef
)。
在某些情况下,只考虑控制序列名称(例如,foo
),而不是控制序列本身(例如,\foo
)会更方便。例如,也许您想要定义一个辅助控制序列,该序列应伴随某些定义,并且为了编码的目的,您使用相同的基本定义。以下是一个例子:
\makeatletter
\newcommand{\foo}{foo}
\newcommand{\foo@bar}{bar}
\newcommand{\print}[1]{\csname #1\endcsname: \csname #1@bar\endcsname}
\makeatother
\print{foo}
打印
foo:酒吧
很容易传递foo
给\print
然后访问\foo@bar
。但是,如果您已传递\foo
给\print
,则必须删除反斜杠\
才能在字符串中添加后缀@bar
并访问\foo@bar
。因此,根据用户(包作者)的不同,使用控制序列名称可能比使用控制序列本身更方便。
etoolbox
\csnumgdef{<var>}{<val>}
明确定义其中一些函数以强制特定用途。
\newcounter{<var>}
\setcounter{<var>}{<val>}
因为<var>
in\csnumgdef
不是计数器。相反,它是一个常规控制序列。但是,由于<val>
使用 进行评估\numexpr
,因此只接受在该上下文中有效的参数。因此,它看起来可能类似于使用计数器,但内部是不同的。由于它使用\numexpr
,您可以执行类似操作\csnumgdef{foo}{5+2}
并将7
存储在 中\foo
。如果您已经定义了计数器,则无需\thefoo
打印数字。