如何更改命令中的 catcode,然后使用新的 catcode 引用输入?

如何更改命令中的 catcode,然后使用新的 catcode 引用输入?

pdfTeX:我正在尝试定义一个命令

  1. 接受输入,
  2. 重新定义字符的 catcode 和功能,并且
  3. 使用新的字符定义打印输入。

我知道这是一项相当复杂的任务,但我猜这是可能的,而且超出了我目前的理解范围。我咨询了答案,效果很好,我的代码就是基于此。我看到处理类似主题的问题,但正如您在我的代码中看到的那样,它并不能满足我的特定要求。

最小(不)工作示例:

\documentclass{minimal}

\def\transfigured#1{\catcode`|=\active% allows | as command
    \begingroup\endlinechar-1%
    \everyeof{{#1}\endgroup}\scantokens{\gdef|}}%

\def\subsumed#1{\begingroup 
      \transfigured{#1} 
      |
    \endgroup}% should simply do the same

\begin{document}
  \begingroup
    \transfigured{test}% gets desired output
    |
  \endgroup

  \subsumed{test}% doesn't
\end{document}

由此可得:

输出 - 命令本身有效,嵌套命令无效

据我所知,\protect其他类似的命令不是问题所在,但感觉这是一个根本性错误。任何帮助都将不胜感激。

编辑:我被要求发布原始函数,以避免 XY 问题。

\documentclass{minimal}
\usepackage{tikz}


\makeatletter
\newsavebox{\internal@inner}
\newlength\arg@ht
\newlength\arg@dp

%%% TRANSFIGURE command: changes appearance of character
\def\transfigure#1#2{\catcode`#1=\active% alters char #1
    \begingroup\endlinechar-1% to #2
    \everyeof{{#2}\endgroup}\scantokens{\gdef|}}%

 %%% DELIM-MID-DELIM command: scans for | in arg
 \def\delimmiddelim#1#2#3#4{% 1: arg 2: left 3: mid 4: right
   \text{\savebox{\internal@inner}{$#1$}%
     \arg@ht=\ht\internal@inner\relax%
     \arg@dp=\dp\internal@inner\relax%
    % minimum height:
    \ifdim\arg@ht<1.2ex\relax
      \arg@ht=1.2ex\relax
    \fi
    % minimum depth:
    \ifdim\arg@dp<.2ex\relax
      \arg@dp=.2ex\relax
    \fi
  % output
  \ensuremath{\begingroup
    \transfigure{|}{#3{\arg@dp}{\arg@ht}}% changes |
    #2{\arg@ht}{\arg@dp}\scantokens{#1}#4{\arg@ht}{\arg@dp}
  \endgroup}}}% returns | to normal use

%%% ANGLED BRACKETS: 
\newcommand\@angl[2]{\ensuremath{\mathopen{\mkern1mu\relax%
\begin{tikzpicture}[scale=1, baseline=0mm, line 
width=.1375mm+.00625*#1+.00625*#2, line cap=round, line join=miter]
\draw (.2mm+.175*#1+.175*#2,.2mm+#1) -- (0mm,.5*#1-.5*#2) -- 
(.2mm+.175*#1+.175*#2,-.2mm-#2);
\end{tikzpicture}%\mkern-5mu\relax
}}}
\newcommand\@angr[2]{\ensuremath{\mathclose{%\mkern-5mu\relax%
\begin{tikzpicture}[scale=1, baseline=0mm, line 
width=.1375mm+.00625*#1+.00625*#2, line cap=round, line join=miter]
\draw (-.2mm-.175*#1-.175*#2,.2mm+#1) -- (0mm,.5*#1-.5*#2) -- 
(-.2mm-.175*#1-.175*#2,-.2mm-#2);
\end{tikzpicture}\mkern1mu\relax
}}}

%%% MIDLINE:
\newcommand\@vertm[2]{\ensuremath{\mathrel{\relax%
  \begin{tikzpicture}[scale=1, baseline=-1.75mm, line 
width=.1375mm+.00625*#1+.00625*#2, line cap=round]
  \draw (0mm,.2mm+#1) -- (0mm,-.2mm-#2);
\end{tikzpicture}\relax
}}}% relation

\newcommand{\brkt}[1]{\delimmiddelim{#1}{\@angl}{\@vertm}{\@angr}}
\makeatother

\begin{document}
\[\brkt{\int|\sum|x}\]
\end{document}

就我而言,它已经可以正常工作了,但欢迎大家提供反馈和改进。输出如下:

\brk{} 的输出

答案1

主要问题可能是|替换文本中的 定义\subsumed未激活。您可以执行\scantokens{|}

\documentclass{minimal}

\def\transfigured#1{\catcode`|=\active% allows | as command
    \begingroup\endlinechar-1\relax
    \everyeof{{#1}\endgroup}\scantokens{\gdef|}}%

% This entire definition is tokenized while | is _not_ active:
\def\subsumed#1{\begingroup 
      \transfigured{#1}%
      \scantokens{|}%
    \endgroup}% should simply do the same

\begin{document}
  \begingroup
    \transfigured{test}% gets desired output
    |%
  \endgroup

  \subsumed{test}%
\end{document}

~顺便说一句:利用 LaTeX 中通常处于活动状态的事实,您|也可以通过\lccode和来获得活动状态\lowercase

\documentclass{minimal}

\def\transfigured{%
  \begingroup
  \lccode`\~=`\|\relax
  \lowercase{\endgroup\def~}%
}%


\def\subsumed#1{%
  \transfigured{#1}%
  \begingroup
  \lccode`\~=`\|\relax
  \lowercase{\endgroup~}%
}%

\begin{document}
\transfigured{test}%
\begingroup\lccode`\~=`\|\relax\lowercase{\endgroup~}%

\subsumed{test}%
\end{document}

学术信息:

您可以(滥用)使用来摆脱由于或LaTeX 中的 -primitive—\futurelet而产生的文件结束标记,并且-primitive 被重命名为:\scantokens\input\input\input\@@input

\documentclass{minimal}

\begingroup\catcode`\%=12\relax
\def\percentchar{\endgroup\def\percentchar{%}}\percentchar

\def\neutralizeoutertoken{%
  \expandafter\transfiguredRemoveEOFMarkerwithArgAsRelax\noexpand
}%
\def\transfiguredRemoveEOFMarkerwithArgAsRelax#1{\let#1=\relax\transfiguredRemoveEOFMarker{#1}}%

\def\transfiguredRemoveEOFMarker#1{%
  \endgroup % <- undo \transfigured's catcode-change before \futurelet 
            %    "looks ahead" and probably triggers tokenization
  \begingroup
  \let#1=\relax
  \def\transfiguredRemoveEOFMarker{\endgroup\def#1}%
  \futurelet\scratchy\transfiguredRemoveEOFMarker
}%  

\def\transfigured{%
  \begingroup
  \catcode`|=\active % allows | as command
  \catcode`\%=14 % allows % as comment
  % active | might be defined \outer ...
  \expandafter\neutralizeoutertoken\scantokens\expandafter{\expandafter|\percentchar}%
}%

\begingroup
\catcode`|=\active
\csname @firstofone\endcsname{%
  \endgroup
  \def\subsumed#1{\transfigured{#1}|}%
}%

\begin{document}

{\catcode`|=\active \outer\gdef|{Huh?}}

\transfigured{test}% define active | with <replacement text> "test"
\begingroup\catcode`|=\active\csname @firstofone\endcsname{\endgroup|}%

\subsumed{test}%

\end{document}

答案2

通过分配 -value,使用类别 11(字母)和 12(其他)的字符标记,\mathcode"8000可以在数学模式中假装它们处于活动状态。

因此,只要的参数\brkt不包含|在 TeX 排版时不会处于数学模式的地方,以下操作就会奏效:

\documentclass{minimal}
\usepackage{tikz}
\usepackage{amsmath}

\makeatletter
\newsavebox\internal@inner

%%% TRANSFIGURE command: changes appearance of character
\newcommand\transfigure[1]{% alters active char #1 to #2
    \begingroup
    \lccode`\~=`#1\relax
    \lowercase{\endgroup\def~}%
}%

%%% DELIM-MID-DELIM command: scans for | in arg
\newcommand\delimmiddelim[4]{% 1: arg 2: left 3: mid 4: right
   \begingroup
   \text{%
     \savebox{\internal@inner}{\ensuremath{#1}}%
     % minimum height:
     \ifdim\ht\internal@inner<1.2ex\relax
       \ht\internal@inner=1.2ex\relax
     \fi
     % minimum depth:
     \ifdim\dp\internal@inner<.2ex\relax
       \dp\internal@inner=.2ex\relax
     \fi
     %-------
     % output
     %-------
     % change the definition of active | :
     \transfigure{|}{#3{\dp\internal@inner}{\ht\internal@inner}}%
     % within math-mode pretend that | is active:
     \mathcode`\|="8000\relax
     \ensuremath{%
       #2{\ht\internal@inner}{\dp\internal@inner}#1#4{\ht\internal@inner}{\dp\internal@inner}%
     }%
  }%
  \endgroup
}%

%%% ANGLED BRACKETS: 
\newcommand\@angl[2]{\ensuremath{\mathopen{\mkern1mu\relax%
\begin{tikzpicture}[scale=1, baseline=0mm, line 
width=.1375mm+.00625*(#1)+.00625*(#2), line cap=round, line join=miter]
\draw ({.2mm+.175*(#1)+.175*(#2)},{.2mm+(#1)}) -- ({0mm},{.5*(#1)-.5*(#2)}) -- 
({.2mm+.175*(#1)+.175*(#2)},{-.2mm-(#2)});
\end{tikzpicture}%\mkern-5mu\relax
}}}
\newcommand\@angr[2]{\ensuremath{\mathclose{%\mkern-5mu\relax%
\begin{tikzpicture}[scale=1, baseline=0mm, line 
width=.1375mm+.00625*(#1)+.00625*(#2), line cap=round, line join=miter]
\draw ({-.2mm-.175*(#1)-.175*(#2)},{.2mm+(#1)}) -- ({0mm},{.5*(#1)-.5*(#2)}) -- 
({-.2mm-.175*(#1)-.175*(#2)},{-.2mm-(#2)});
\end{tikzpicture}\mkern1mu\relax
}}}

%%% MIDLINE:
\newcommand\@vertm[2]{\ensuremath{\mathrel{\relax
  \begin{tikzpicture}[scale=1, baseline=-1.75mm, line 
width=.1375mm+.00625*(#1)+.00625*(#2), line cap=round]
  \draw ({0mm},{.2mm+(#1)}) -- ({0mm},{-.2mm-(#2)});
\end{tikzpicture}\relax
}}}% relation

\newcommand{\brkt}[1]{\delimmiddelim{#1}{\@angl}{\@vertm}{\@angr}}
\makeatother

\begin{document}
\[\brkt{\int|\sum|x}\]
\end{document}

在此处输入图片描述



附录回答了2003 年 12 月 14 日的评论

> 我很欣赏这些改进。

判断这些变化是否被视为改进取决于您。;-)

> 你能解释一下吗
> (1)为什么改变数学代码更好?

判断这是否更好取决于你。;-)

让我们提一下一些副作用\scantokens

\scantokens以 ⟨balanced text⟩ 作为参数,并假装将形成 ⟨balanced text⟩ 的标记未扩展地写入外部文本文件,然后通过 -primitive 处理该外部文本文件\input

(我说“假装”是因为,不是创建文本文件,而是使用计算机易失性内存的某个区域。

与 '不同的是\write \scantokens,假装的写作没有字符翻译/没有转换为^^-符号,也没有在计算机平台的字符编码方案中重新编码。关键字“字符翻译”与 MiKTeX 或 TeX Live 等 TeX 发行版的 Web2C 实现手册中提到的这些 .tcx 文件有关。

通过以下方式处理文本文件\input意味着(步骤 1)当保存最后一行尚未处理的字符的缓冲区为空而需要更多字符时,读取文本文件中的一行;并且(步骤 2)当需要更多标记时,将该行中尚未处理的字符作为生成标记的指令并将其附加到标记流中。

通过读取一行\input文本并填充缓冲区来处理文本文件,缓冲区中保存着最后一行读取的尚未处理的字符,并进行一些预处理,将来自文件的字符从计算机平台的字符编码方案转换为 TeX 的内部字符编码方案。

使用\scantokens' 假装输入在预处理阶段从计算机平台的字符编码方案转换为 TeX 的内部字符编码方案会省去一行 .tex-input。这是因为这里的字符已经按照 TeX 的内部字符编码方案进行了编码。)

\scantokens' 假装书写具有与 TeX 通过以下方式将未扩展的标记写入真实文本文件或屏幕相同的特性\write

  • 例如,在书写时,类别 6(参数)的显式字符标记会加倍。
    因此,\scantokens通常哈希值也会加倍。
  • 例如,在编写控制字标记时,即,其名称由当前类别代码为 11(字母)的单个字符组成或由任意类别代码的几个字符组成的控制序列标记,会附加一个空格字符。

\scantokens' 假装通过处理\input意味着所做的事情(几乎)与 TeX 逐行读取 .tex 输入文件并在需要标记时将该行的字符作为创建标记的指令时所做的事情相同:

  • 当到达伪装文件的末尾时,构成 ⟨token 参数⟩ 值的标记\everyeof将附加到标记流中。

  • 标记\scantokens化从开始执行时当前的分类代码机制开始\scantokens。(如果构成参数的⟨平衡文本⟩\scantokens包含被标记化为改变分类代码机制的指令的内容,然后这些指令得到执行,这些变化可能会影响来自该⟨平衡文本⟩的后续内容的标记化方式。)

开始执行时当前的分类代码制度\scantokens不一定与对构成⟨平衡文本⟩/的论据的标记进行标记时当前的分类代码制度相同\scantokens

因此,如果你这样做

\scantokens{#}%

,你会得到标记—表示一个明确的字符标记,类别为 10(空格)且字符代码为 32,它是由于 TeX 的-token 机制以及 TeX 的读取设备在通过标记化生成后处于状态 M(行中间)的情况而产生的。通俗地说,这样的-token 叫做 空格标记。如果在 TeX 以水平模式或受限水平模式排版时处理空格标记,则空格标记会产生水平粘合。除非在特殊情况下将它们删除,例如,当 TeX 正在收集构成 TeX ⟨number⟩ 数量的组成部分的标记时,或者在宏的两个未限定参数之间,作为 ⟨optional space⟩。 此外,构成 ⟨token param⟩ 当前值的标记被附加到标记流中。#6#61010\endlinechar#610
\everyeof

如果你

 \makeatletter\def\macro{\scantokens{\@macro}}\makeatother [...] \macro

\macro,则之后执行的实例\makeatother不会产生单个标记\@macro,但会产生标记。 此外,构成 ⟨token 参数⟩ 当前值的标记将附加到标记流中。\@m11a11c11r11o1110
\everyeof

如果你

\catcode`\A=12\relax

,则 .tex-input\ABC被标记化为。\AB11C11

如果你

\catcode`\A=12\relax
\def\macro{\scantokens{\ABC}}%
\macro
\catcode`\A=11\relax
\macro

,则 的第一个实例\macro会产生标记。 此外,构成 ⟨token 参数⟩ 当前值的标记会附加到标记流中。\AB11C1110
\everyeof

的第二个实例\macro产生控制字 token \ABC
此外,构成 ⟨token 参数⟩ 当前值的 token\everyeof被附加到 token 流中。

> 您提到它在非数学环境中排版适当的符号,但最初,保存框被设置为输入处于数学模式。

我想我提到过通过(本地)更改来执行操作只适用于 TeX 处于数学模式时排版的内容。\mathcode;-)|

有人可能会做一些奇怪的事情,例如\[\brkt{\int\hbox{text with |}|\sum|x}\]。在数学模式下不会处理|in ,但在受限水平模式下不会处理,因为-change-thingie 不适用,因此不会被视为处于活动状态。\hbox{text with |}\mathcode|

我认为这可能是一个优势,因为看起来你|只希望在数学模式下有特殊行为。
但判断这一点取决于你。

>(2)为什么不定义新的长度更好?

这是个人喜好问题。节省一些长度的寄存器和分配。

> 这种新配方是否不会潜在地扩大盒子的内容?

它不会改变/拉伸内容,它只会改变框的边框。

如果您减小高度/深度/宽度,东西仍会未拉伸。但其中一些东西可能会伸出盒子的边框/可能伸出盒子的面积。但是,盒子的边框是放置其他盒子或周围胶水或周围\kern或周围的相关标准\leaders。因此,如果盒子的材料伸出盒子的边框,则可能导致伸出的材料与其他盒子中的材料重叠。

如果增加高度/深度/宽度,东西仍然没有被拉伸。但构成盒子“内部”的面积被认为更大。

\fbox您可以通过在框周围放置一个并将其\fboxsep设置为 0pt 来获得框尺寸的可见印象。(\fboxsep是框的(不可见)边框与框周围绘制的框架的(可见)线条/规则之间的空白空间的距离/宽度。)

以下 LaTeX 2ε 代码片段将\mybox一个框放入框寄存器中,您可以在其中看到黑色框的基线和一些精彩文本。

首先,这个盒子以不变的盒子尺寸进行排版,同时通过 在其周围放置一个红色框架\fbox

然后,该框的尺寸会减小/增大,并再次排版,同时通过 在其周围放置一个红色框架\fbox

红色框架表示被盒子覆盖的区域。

\documentclass[border=1cm]{standalone}
\usepackage{color}

\newsavebox\mybox

\begin{document}

\huge

\savebox\mybox{%
  \hbox{%
    \savebox\mybox{\hbox{Some great text}}\usebox\mybox\llap{%
    \hbox to\wd\mybox{\leaders\hrule height .8pt \hfill \kern 0pt }}%
  }%
}%

\begin{minipage}{27.7cm}%
\color{red}%
\fboxsep=0pt
% Switch to horizontal mode, i. e., the mode where TeX does the breaking of paragraphs
% into lines for you automatically:
\leavevmode
% Draw red lines showing the baseline of the surrounding line of text:
\hrulefill
% Place the box on the baseline of the surrounding line of text
% and have TeX draw a red frame at the borders of the box:
\fbox{\usebox\mybox}%
% Draw red lines showing the baseline of the surrounding line of text:
\hrulefill
% Decrease the measurements of the box:
\ht\mybox=.5\dimexpr\ht\mybox\relax
\dp\mybox=.5\dimexpr\dp\mybox\relax
\wd\mybox=.5\dimexpr\wd\mybox\relax
% Place the box on the baseline of the surrounding line of text
% and have TeX draw a red frame at the borders of the box:
\fbox{\usebox\mybox}%
% Draw red lines showing the baseline of the surrounding line of text:
\hrulefill
% Increase the measurements of the box:
\ht\mybox=4\dimexpr\ht\mybox\relax
\dp\mybox=4\dimexpr\dp\mybox\relax
\wd\mybox=4\dimexpr\wd\mybox\relax
% Place the box on the baseline of the surrounding line of text
% and have TeX draw a red frame at the borders of the box:
\fbox{\usebox\mybox}%
% Draw red lines showing the baseline of the surrounding line of text:
\hrulefill\null
\end{minipage}%

\end{document}

输出为:

在此处输入图片描述

红色框架表示被视为盒子的边界。

红线表示 TeX 在这些框之外考虑的内容。

盒子的黑色内容不会拉伸/收缩,但减小高度/深度/宽度会导致某些内容超出盒子的边框,因此位于盒子之外,因此在计算其他材料的位置时不会被考虑。增加高度/深度/宽度会导致放置盒子内容的区域以及使盒子“内部”被视为更大的区域。

如果你想缩小/拉伸盒子的内容,你可以使用\scalebox包的命令图形

> (3)为什么小写字母技巧更好?

判断它是否更好取决于你。

\lowercase而不是你\scantokens不会遇到潜在的麻烦有关

  • TeX 对未扩展的书写标记(哈希加倍、将空格字符附加到控制字标记)以及在不同类别代码制度下重新标记的微妙之处,可能会产生一组与原始标记不同的标记,而这些标记的方式并非我们所期望的。
  • 摆弄 ⟨token 参数⟩ \everyeof。(如果 的参数\brkt本身包含另一个\scantokens指令,而你不希望更改 的值,\everyeof该怎么办?你需要在\everyeof的参数内部本地撤消更改\brkt...)

> (4) 为什么要用括号和圆括号来改变 tikzpictures?这只是为了美观吗?

括号用于确保以正确的顺序执行数学运算,以防参数本身包含不仅一个标记,\arg@ht而是包含多个标记,如\ht\internal@inner或形成数学表达式。

花括号是为了确保括号界定逗号分隔的内容,

(..., ...) -- (..., ...)

,不会被所表示的数学表达式的括号错误地匹配...

所以就像

({...},{...}) -- ({...},{...})

以确保()内部不会错误地与或外部{...}匹配。(){...}

答案3

评论太长了。

你只需要一个数学活动|,不需要\scantokens

\brkt使用下面的代码可以毫无问题地嵌套命令。

\documentclass{article}

\NewDocumentCommand{\brkt}{m}{%
  \begingroup
  \activatebar
  \mathcode`|="8000
  \left\langle
  #1
  \right\rangle
  \endgroup
}

\newcommand{\activatebar}{%
  \begingroup\lccode`~=`|\lowercase{\endgroup\gdef~}{\:\middle|\:}%
}

\begin{document}

\[
\brkt{\int|\sum_{k=1}^n \brkt{a_k|c_k} | x}
\]

\end{document}

在此处输入图片描述

相关内容