我是 LaTeX 的新手,所以这个问题可能显得比较基础。它也可能反映了我从事 C/C++ 编程时的偏见/假设。
我查看了“考试”类的代码,发现有些\if<xyz>
有@
,而有些没有。
例如,下面是类文件中的几行代码(按出现/执行的顺序)
\newif\ifcancelspace
\newif\if@addpoints
\def\addpoints{\global\@addpointstrue}
\@addpointsfalse
我无法理解以下内容:
是否使用(在 [4] 中声明的)
\if@addpoints
的值对 [2] 中的(当它出现时)进行求值@addpoints
在上面的添加点前面添加一个有意义吗
@
?- 在类/包文件中命名变量只是良好的编程习惯吗?
\def\addpoints
或者,这是必要的,因为[3] 中有另一个,并且@
需要区分两者?
LaTeX 中变量的默认范围是什么?
@addpoints
当它直到 [4] 才被定义时,如何在 [3] 中使其成为全局的?
答案1
这个问题同时涉及几件事,这使得回答它变得有点有趣。其他人采取了一种策略,我将采取另一种策略。
首先,请记住 TeX 是一种宏扩展语言,而不是函数式语言。其次,请注意 TeX 的内置变量类型非常少。因此,许多“变量”都是具有适当结构的宏。
该\newif
宏根据参数创建三个新的宏\if<name>
:
\if<name>
,测试时使用的开关;\<name>true
,将开关设置为逻辑真;\<name>false
,将开关设置为逻辑上的假;
通常,\if<name>
这里是类似\if@myswitch
或的东西\ifmy@switch
。其他答案提到@
这是一个“字母”,用于命名 TeX 宏。因此,@
对于 TeX 来说,没有特殊含义:它只是为程序员而存在。(通常的模式是使用@
以使名称更易于阅读,因此\if@<package>@<meaning>
或\if<package>@<meaning>
很常见。)
因此,分析问题,这两行\newif\ifcancelspace
并\newif\if@addpoints
创建宏
\ifcancelspace
\cancelspacetrue
\cancelspacefalse
\if@addpoints
\@addpointstrue
\@addpointsfalse
这两个开关现在可以在建筑中使用
\if@addpoints % or \ifcancelspace
% Do stuff
\else
% Do other stuff
\fi
接下来,以点 [4] 为例,宏将开关\@addpointsfalse
设置\if@addpoints
为逻辑上的 false。因此,这意味着我上面的测试将“执行其他操作”。设置\@addpointstrue
意味着测试将“执行操作”。
最后要处理的一点是 [3],为此你需要了解 TeX 中的分组、宏扩展以及开关的实际工作方式。由于 TeX 是一种宏语言,因此它没有在函数“内”使用变量的概念。因此,组是由构造创建的
{ ... }
或者
\begingroup ... \endgroup
(括号组也用于 TeX 需要分组的地方,因此它实际上消失了。例如,需要使用的组就是这种情况\def
。)
除非是全局赋值,否则赋值将被困在诸如组内。现在,定义\def\addpoints{\global\@addpointstrue}
意味着我们使用的地方\addpoints
,TeX 会将其替换为(宏扩展)。我们稍后\global\@addpointstrue
会回到,但首先要注意的是,这是一个宏,它扩展为\global
\@addpointsture
\let\if@addpoints\iftrue
这是一个赋值,通常仅适用于当前 TeX 组级别。但是,\global
前缀表示赋值忽略分组。因此结果是\addpoints
将全局将开关设置\if@addpoints
为逻辑上为真。
答案2
虽然这没有按顺序回答您的问题,但它确实为您呈现的代码 [1]-[4] 提供了一些指导。
@
在 LaTeX 中是保留符号,因此不会像其他字母那样处理。执行\makeatletter
会反转此保留,“生成@
字母”,因此可以在常规变量中使用。当然,\makeatother
会恢复此更改。因此\newif\if@addpoints
指定一个名为 的新布尔值@addpoints
。执行\@addpointstrue
/\@addpointsfalse
将其设置为 true/false。
类似地,提供了一个可以以类似方式(或)修改的\newif\ifcancelspace
布尔值。我认为在变量中使用背后的动机为宏程序员增加了一个深度层。例如,它允许程序员(比如说)使用类似的东西在所有变量前加上包名前缀。如果是一个非常常见但描述性的变量,其他包可能会与这种重新定义发生冲突。但是,使用前缀可以避免这种情况。所以,从某种意义上说,由于存在大量的包和非常不同的编程风格(以及变量/宏的使用),添加这一层特异性是一种很好的编程习惯。cancelspace
\cancelspacetrue
\cancelspacefalse
@
\newif\myfunc@dothis
dothis
LaTeX 中变量的默认作用域是它们被声明/修改的组的局部范围。在赋值语句前加上前缀\global
会使更改变为全局的,因此存在于进行赋值的组之外。例如,考虑\def\addpoints{\global\@addpointstrue}
。这定义了一个名为 的宏\addpoints
(通过使用\def
,并且该宏执行\global\@addpointstrue
。从上面的讨论应该可以清楚地看出,这会将布尔值设置@addpoints
为 true。但是,由于宏定义包含在大括号 中{ }
,因此它定义了一个以 开头{
并以 结尾的组}
。在此组内所做的任何更改/赋值在其外部都是无效的。由于您使用了\global
,这会覆盖作用域以扩展到组之外。也就是说,\@addpointstrue
不是“全局的”。相反,修改的作用域是全局扩展的。
具体到最后一条:\newif\@addpoints
是布尔值 的定义@addpoints
,而\global
调整范围(如前所述)。从编程的角度来看, 的使用global
可能与变量声明有关。然而,在 LaTeX 中并非完全如此。事实上,它可以在变量声明时使用(以便使声明成为全局声明,如 ,顺便说一下,\global\def\mycommand{...}
这相当于\gdef\mycommand{...}
)或在某些变量修改期间使用。
答案3
该@
符号在 LaTeX 中经常(但并非唯一)用作“特殊字符”,这意味着(大多数情况下)任何包含包含此字符的命令的宏都必须处于“特殊模式”(好吧,这听起来有点循环!)才能工作。使用命令进入“特殊模式”,\makeatletter
使用命令退出\makeatother
。LaTeX 内核和许多 LaTeX 软件包定义了供内部使用的命令,这些命令不应在普通编程中直接访问,即由用户访问。为了避免意外使用这些内部命令,通常会为它们提供一个或多个@
符号;理论上,用户要访问此类命令,他们必须先明确提供命令,\makeatletter
然后才能这样做,在这种情况下(理论上是这样)他们知道自己在做什么……
您提供的示例说明了这一点:指令\newif\if@addpoints
创建一个名为的布尔变量,还有什么@addpoints
;这是一个内部变量,通常不应直接操作;其默认值为语句的“false” 。另一方面,\@addpointsfalse
命令是一个用户级命令,可让您将此变量的状态更改为“true”。\addpoints
当然,这种编程约定并不是完全万无一失的,但经验表明,遵循此约定的代码更加健壮。