\csname 和 \endcsname 到底起什么作用?

\csname 和 \endcsname 到底起什么作用?

他们到底做什么\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}将根据foovia定义\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\themycountr@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”。%符号的存在是为了避免不必要的空格进入我们正在构建的宏的名称。

相关内容