我正在阅读有关 catcode 的文章,我想尝试一下。但是,有一点我不太明白。当我想定义一个活动字符(例如方括号 ([
和]
))时,我会执行以下操作:
\catcode`[ = \active
\catcode`] = \active
进而
\def[{blah}
\def]{pfff}
因此[]
将打印blahpfff
。但是,我无法转义方括号,例如在 ConTeXt\blank[argument]
或 LaTeX中\includegraphics[key-value pairs]{cuteimage}
。我试过了
\let\amacro=[
\let\anothermacro=]
这仅适用于打印字符,而不适用于替换以下命令中的方括号,例如\blank\amacro small\anothermacro
。为什么它不起作用?顺便说一句,我知道 catcode 很棘手,但我还是想了解它们的工作原理。
答案1
当定义一个带有可选参数的命令时,比如(简化)
\newcommand{\includegraphics}[2][]{...}
其中,...
是用于评估可选参数中的材料的代码,而强制参数中的语法\includegraphics
则是
\includegraphics[<options>]{filename}
或者
\includegraphics{filename}
当\includegraphics
被调用时,TeX 会查看下一个字符;如果是[
12(下标表示类别代码),则采用一条路径,并调用一个使用分隔参数定义的宏。否则,将调用另一个带有标准参数的宏。
因此,如果你将类别代码更改[
为 13(活动),则调用
\includegraphics[<options>]{filename}
将导致 TeX不是选择第一条路径,因为[
12 没有跟随;而是调用单个参数宏,导致活动[
成为要包含的图形的文件名。
结论:不当然,使其[
处于活动状态,并且既不活动也不活动。]
更多细节。当你这样做时
\newcommand{\foo}[2][baz]{Something with #1 and #2}
基本工作原理如下。LaTeX 定义二宏,即\foo
和\\foo
(名称中带有反斜杠,我将\sfoo
在下文中使用它来提高清晰度);前者由
\def\foo{\@protected@testopt\foo\sfoo{bar}}
后者
\def\sfoo[#1]#2{Something with #1 and #2}
内核宏\@protected@testopt
定义如下
% latex.ltx, line 885:
\def\@protected@testopt#1{%
\ifx\protect\@typeset@protect
\expandafter\@testopt
\else
\@x@protect#1%
\fi}
在标准排版中,遵循“真”分支,因此\@testopt
称为
% latex.ltx, line 883:
\long\def\@testopt#1#2{%
\kernel@ifnextchar[{#1}{#1[{#2}]}}
[
这里是对12进行测试的地方。在 的替换文本中,\@testopt
有一个[
,它在定义时已被标记,类别代码为 12。它不会识别任何另一个标记与 相同[
。而[
13是另一个标记。
答案2
分解为要点/基本思想可以说:
对于按照\newcommand
该处理可选参数的方式定义的 LaTeX 宏,为了确定可选参数是否存在,在处理的某个阶段\kernel@ifnextchar
,将使用 LaTeX 2ε 包装器\futurelet
来测试是否存在一个标记,其含义等同于类别代码 12(其他)的显式开方括号字符标记的含义。
然后,通过将控制序列标记插入到标记流中来调用第二个宏,该标记的名称等于通过定义的命令的名称,\newcommand
但前面有一个反斜杠。例如,使用\newcommand\foo[2][optional-default]{...}
,这将是\\foo
。第二个宏的定义对应于相应 -directive 提供的定义\newcommand
。与第二个宏 ( \\foo
) 相对应的控制序列标记由类别代码 12(其他)的显式左方括号字符标记分隔。该宏的第一个参数由类别代码 12(其他)的显式右方括号字符标记分隔。
如果\kernel@ifnextchar
测试表明没有可选参数,则调用第二个宏,并在相应的控制序列标记后面附加一个标记序列,其结构如下:类别代码为 12(其他)的开方括号字符标记、默认值、类别代码为 12(其他)的显式闭方括号字符标记。
如果\kernel@ifnextchar
测试表明可能有一个可选参数,则调用第二个宏而不附加任何额外的标记。
对于处理分隔参数的宏,定义的参数文本中构成参数分隔符的标记不能被具有相同含义的其他标记替换。
这是因为当 TeX 扫描参数分隔符时,TeX 会“查看”标记,而不是标记的含义。
-macro\\foo
导致 TeX 扫描参数分隔符和。[12
]12
比如
\let\amacro=[
\let\anothermacro=]
\foo\amacro optional value \anothermacro...
您需要要求 TeX 在扫描参数分隔符时不要“查看”标记,而是“查看”标记的含义。
在某些情况下,您可以采取一些\uppercase
- 或 -\lowercase
技巧 -\uppercase
并\lowercase
在更改字符代码时保持显式字符标记的类别代码不变,因此您可以执行如下操作:
\documentclass[landscape, a4paper]{article}
%%%%%%% Layout %%%%%%%%%%%%%%%%%%%%%%%%%%%%
\csname @ifundefined\endcsname{pdfpagewidth}{}{\pdfpagewidth=\paperwidth}
\csname @ifundefined\endcsname{pdfpageheight}{}{\pdfpageheight=\paperheight}
\csname @ifundefined\endcsname{pagewidth}{}{\pagewidth=\paperwidth}
\csname @ifundefined\endcsname{pageheight}{}{\pageheight=\paperheight}
\textwidth=\paperwidth
\oddsidemargin=1.5cm
\marginparsep=.2\oddsidemargin
\advance\textwidth -2\oddsidemargin
\marginparwidth=\oddsidemargin
\advance\oddsidemargin-1in
\evensidemargin=\oddsidemargin
\advance\marginparwidth-2\marginparsep
\textheight=\paperheight
\topmargin=1.5cm
\advance\textheight-2\topmargin
\footskip=.5\topmargin
\advance\topmargin-1in
\headheight=0pt
\headsep=0pt
{\normalfont
\csname @tempdima\endcsname=.5\ht\strutbox
\expandafter}\expandafter\advance\expandafter\footskip\the\csname @tempdima\endcsname
{\normalfont\expandafter}\expandafter\topskip\expandafter=\the\ht\strutbox
\pagestyle{plain}%
\parindent=0ex
\parskip=\baselineskip
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\newcommand\macrowithoptionalarg[1][default]{The things inside the parentheses are taken for the argument: (#1)}
\begin{document}
Test 1: \macrowithoptionalarg
Test 2: \macrowithoptionalarg[{non-default}]%
\begingroup
\catcode`[ = \active
\catcode`] = \active
\def[{blah}
\def]{pfff}
%...
\begingroup
% catcode of ( is 12 and catcode of ) is 12.
\lccode`\(=`\[
\lccode`\)=`\]
% Now applying \lowercase to ( yields [ of catcode 12 and applying \lowercase to ) yields ]
% of catcode 12, but be aware that AND gets lowercasd also:
Test 3: \lowercase{\endgroup\macrowithoptionalarg({[ AND ]})}%
%...
\endgroup
\end{document}
如果允许使用\edef
和 ε-TeX ,您可以申请将 active-和 active-转换为其 category-code-12-pendants:\unexpanded
\string
[
]
\documentclass[landscape, a4paper]{article}
%%%%%%% Layout %%%%%%%%%%%%%%%%%%%%%%%%%%%%
\csname @ifundefined\endcsname{pdfpagewidth}{}{\pdfpagewidth=\paperwidth}
\csname @ifundefined\endcsname{pdfpageheight}{}{\pdfpageheight=\paperheight}
\csname @ifundefined\endcsname{pagewidth}{}{\pagewidth=\paperwidth}
\csname @ifundefined\endcsname{pageheight}{}{\pageheight=\paperheight}
\textwidth=\paperwidth
\oddsidemargin=1.5cm
\marginparsep=.2\oddsidemargin
\advance\textwidth -2\oddsidemargin
\marginparwidth=\oddsidemargin
\advance\oddsidemargin-1in
\evensidemargin=\oddsidemargin
\advance\marginparwidth-2\marginparsep
\textheight=\paperheight
\topmargin=1.5cm
\advance\textheight-2\topmargin
\footskip=.5\topmargin
\advance\topmargin-1in
\headheight=0pt
\headsep=0pt
{\normalfont
\csname @tempdima\endcsname=.5\ht\strutbox
\expandafter}\expandafter\advance\expandafter\footskip\the\csname @tempdima\endcsname
{\normalfont\expandafter}\expandafter\topskip\expandafter=\the\ht\strutbox
\pagestyle{plain}%
\parindent=0ex
\parskip=\baselineskip
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\newcommand\macrowithoptionalarg[1][default]{The things inside the parentheses are taken for the argument: (#1)}
\begin{document}
Test 1: \macrowithoptionalarg
Test 2: \macrowithoptionalarg[{non-default}]%
\begingroup
\catcode`[ = \active
\catcode`] = \active
\def[{blah}
\def]{pfff}
%...
\edef\scratchmacro{\unexpanded{\macrowithoptionalarg}\string[\unexpanded{[ and ]}\string]}%
Test 3: \scratchmacro
%...
\endgroup
\end{document}
答案3
我正在阅读这个问题中的更多可能性,但似乎字符的主动定义最有吸引力的用途是在数学模式下。对此有支持!
\begingroup
\catcode`[ = \active
\catcode`] = \active
\gdef[{blah}
\gdef]{pfff}
\endgroup
% catcodes are back to normal, but the definitions remain.
% special setting to use the definition in math mode
\mathchardef`[="8000
\mathchardef`]="8000
现在变体 catcode 的问题已经消失,但是在数学公式中使用定义的含义,但在文本模式下使用纯字符。