他们到底做什么\csname
?\endcsname
他们的工作是什么?
我浏览过 Texbook 和其他一些书,但没有一本对我来说足够清楚。
有人能举一个简单的例子来澄清这个问题吗?
答案1
通常,控制序列名称仅由字母或一非字母字符。
更准确地说,字母在读取控制序列名称时是具有类别代码 11 的字符。因此,任何字符都可以成为控制序列名称的一部分,只要我们在定义和每次使用之前更改其类别代码即可。
这样\csname...\endcsname
我们就摆脱了这个限制,每个字符都可以进入它们内部形成一个控制序列名称(当然,%
被排除在外,因为它在 TeX 对字符进行工作之前与行上剩余的内容一起消失)。
但是,这并不是 的主要目的\csname...\endcsname
。此构造用于从“可变部分”构建命令。例如,想想 LaTeX 的\newcounter
: 在 之后\newcounter{foo}
,TeX 知道\thefoo
正是以这种方式构建的。粗略地说,LaTeX 所做的是
\newcommand{\newcounter}[1]{%
\expandafter\newcount\csname c@#1\endcsname
\expandafter\def\csname the#1\endcsname{\arabic{#1}}%
}
这样就\newcounter{foo}
可以了。当然,情况比这更复杂,但主要内容在这里;\newcount
是分配计数器的低级命令。这只是在查看令牌\expandafter
之前构建控制序列。\newcount
\def
在里面\csname...\endcsname
,类别代码并不重要(有一个主要例外:活动角色将要如果前面没有\string
,则展开,见最后说明)。LaTeX 利用这一点来构建用户无法(轻松)访问的控制序列名称。例如,选择默认十点字体的控制序列是\OT1/cmr/m/n/10
,它可以轻松地在内部拆分(通过“反向”操作\string
),并且普通用户无法使用。
另一个重要用途是在环境中:当您说 时\newenvironment{foo}
,LaTeX 实际上定义\foo
和\endfoo
。在找到 后\begin{foo}
,LaTeX 会进行一些簿记,然后执行\csname foo\endcsname
(这就是为什么可以说\newenvironment{foo*}
);类似地,在\end{foo}
LaTeX 执行\csname endfoo\endcsname
时,它在此之后再次进行一些簿记。
其他用途:\label{foo}
将根据foo
via定义\csname...\endcsname
可使用的控制序列\ref
。
当有人说 时\csname foo\endcsname
,LaTeX 会检查 是否\foo
定义;如果没有,它将执行\relax
并从此(尊重分组)开始,\foo
将被解释为\relax
。此功能的一个有趣用法是可以说
\chapter*{Introduction}
\csname phantomsection\endcsname
\addcontentsline{toc}{chapter}{Introduction}
hyperref
如果包已加载,则保持正常状态,如果包未加载,则不执行任何操作。
这个技巧还有许多其他有趣的用途。但我们应该始终记住,TeX 不会完全扩展它在这种背景下发现了什么,并且仅限字符必须保留。所以
\csname abc\relax def\endcsname
是被禁止的。但是,之后\def\xyz{abc}
,
\csname \xyz def\endcsname
将是合法的,并且相当于说\csname abcdef\endcsname
或\abcdef
。
最后说明
最好添加一些关于类别代码的内容。 中的活动字符\csname...\endcsname
将被扩展,因此要获得文字,~
必须写入\string~
。注释(类别 14)、忽略(类别 9)和无效(类别 15)字符将保留。所以
\csname %\endcsname
会出现错误(缺失\endcsname
);\csname ^^@\endcsname
不字符并\csname ^^?\endcsname
会引发错误。
答案2
作为参考,来自 TeX Book(格式略有变化),第 7 章:TeX 如何读取你输入的内容(第 40 页):
...您可以通过说 来从字符标记列表转到控制序列。出现在和
\csname<tokens>\endcsname
之间的此构造中的标记可能包含其他控制序列,只要这些控制序列最终扩展为字符而不是 TeX 基元;最终的字符可以是任何类别,不一定是字母。例如,本质上与 相同;但是 是非法的,因为扩展为包含基元的标记。此外, 将产生不寻常的控制序列,即标记,您通常无法写入。\csname
\endcsname
\csname TeX\endcsname
\TeX
\csname\TeX\endcsname
\TeX
\kern
\csname\string\TeX\endcsname
\\TeX
<\TeX>
我用过这个间接通过使用\label
-\ref
系统并根据计数器定义标签:
\newcounter{mycount}
%...
\newcommand{\mycmd}{%
\stepcounter{mycount}%
\label{abc\themycount}%
%...
}
这样,每次调用 时都会创建一个“连续标签abc1
, , ... ” ,以避免创建具有相同名称的多个定义标签。间接地,调用,它又调用abc2
\mycmd
\label{abc\themycount}
\@namedef{r@abc\themycount}
\expandafter\def\csname r@abc\themycount\endcsname
从而扩展r@abc\themycount
为r@abc1
并定义\r@abc1
第一个标签、\r@abc2
第二个标签等。是的,LaTeX 中的标签实际上是以 为前缀的控制序列,r@
并使用 构造,\csname ... \endcsname
然后允许使用数字。
答案3
\csname
/\endcsname
允许您构建名称包含 1. 非字母(例如点、冒号或数字)的命令,更重要的是 2. 定义或使用命令时会扩展的命令。如果您想从各种信息中构建命令名称,这两者都很有用。
举个例子:xskak-Package 循环遍历棋局符号,并在命令中存储每一步的大量信息。它的代码包含大量这种类型的定义:
\expandafter\xdef
\csname Xskak.\xskak@val@gameid.\the\c@move.\WhiteToMove{w}{b}.piece\endcsname{%
....
}
其中\xskak@val@gameid
是当前游戏的 ID,\the\c@move
给出当前移动编号,\WhiteToMove{w}{b}
根据当前移动的玩家给出 w 或 b。因此,在 ID 为“mygame”的游戏中,黑棋第 10 步移动时,这\xdef
定义了一个“命令” \Xskak.mygame.10.b.piece
,其中包含黑棋在第十步移动的棋子的名称。
答案4
简短回答:\csname
和\endcsname
是一个“宏环境”,如果它们的内容(在扩展宏之后)评估为“普通文本”,则它们将转换为宏的名称(或控制序列,因此为“csname”)。
实际上,根据 LaTeX 宏的规则,你实际上可以编写代码,\begin{csname}...\end{csname}
并且它会在一定程度上按照你的预期运行,这也许是一个奇怪的笑话。例如:
\def\macro{text}
\def\o{o}
\begin{csname}%
macr\o
\end{csname}
% Same as \csname macr\o \endcsname
(当使用latex
而不是运行时tex
)将在输出中产生单词“text”。%
符号的存在是为了避免不必要的空格进入我们正在构建的宏的名称。