假设一个标记寄存器包含一系列字母标记,每个字母标记有catcode
12 个。我想在第二个标记寄存器中存储相同的字母标记序列,并catcodes
在过程中进行一些更改(例如更改所有内容#
以使它们具有 catcode 6)。
我认为有三种方法可以实现此目的:
使用迭代器遍历标记序列
futurelet
并进行替换。此方法的不便之处在于,在标记寄存器末尾添加标记需要进行大量扩展和解析,但其优点在于可以推广。使用带有参数模板的宏(就像我们用来查看标记是否在序列中的方法一样)来快速解析标记寄存器。它的缺点是可能不容易推广。
将标记写入某个辅助文件中,重新配置词法分析器并重新读取这些标记。这很丑陋。
有没有更简单的方法?(使用纯 TeX,没有 Lua,没有 Perl,没有 OCaml……)
答案1
我把这个问题当作一个学习的机会,而不是真正试图回答你的问题,所以我的答案还不是一种通用的方法(尽管我认为只要稍微努力就可以达到这个目的)。我会说这种方法属于你的第 2 类,“使用带有参数模板的宏(就像我们用来查看标记是否在序列中的方法一样)来快速解析标记寄存器。”
我不知道如何测试我是否成功操作了#
符号的 catcode,因此我选择了一个不太具挑战性的问题,即下划线_
,我知道我可以将它放在一些美元符号之间并立即知道我是否成功更改了 catcode。
解决方案已编辑,因此\iterationengine
现在是递归的,并将持续到所有 catcode12 下划线都被替换为 catcode8 下划线。最初,我必须手动依次调用迭代引擎。
在下面的 MWE 中,设置我的宏后,我在四个单独的字符串上对其进行测试,包含 0、1、2 和 3 个下划线。
我还无法确认,但检测器程序(\detectus
)可能如果遇到 catcode12 下划线,后面跟着正常的 catcoded \testchar
(在本例中为\relax
),则会失败。
\documentclass{article}
\usepackage[T1]{fontenc}
\def\testchar{\relax}
\def\usEight{_}
\catcode`_=12
\def\detectus#1_#2\relax{%
\def\Detected{#2\testchar}\if\testchar\Detected\else\def\Detected{T}\fi}
\def\changeus#1_#2\relax{#1\usEight#2}
\makeatletter\def\iterationengine{%
\if T\Detected%
\expandafter\detectus\catEightTok_\relax%
\if T\Detected\protected@edef\catEightTok{\expandafter\changeus\catEightTok\relax}\fi%
\iterationengine%
\fi%
}\makeatother
\catcode`_=8
\def\iteration{\let\catEightTok\catTwelveTok\def\Detected{T}\iterationengine}
\long\def\runtest{%
\makebox[3cm][l]{$\rawtok$} raw token in math\par
\edef\catTwelveTok{\detokenize\expandafter{\rawtok}}
\makebox[3cm][l]{\catTwelveTok} detokenized raw token (cat12)\par
\makebox[3cm][l]{$\catTwelveTok$} cat12 token in math\par
\iteration
\makebox[3cm][l]{\detokenize\expandafter{\catEightTok}}
detokenized converted token (cat 8)\par
\makebox[3cm][l]{$\catEightTok$} converted token, in math
}
\begin{document}
\vspace{1ex}\def\rawtok{ab}\runtest\par
\vspace{1ex}\def\rawtok{a_b}\runtest\par
\vspace{1ex}\def\rawtok{a_b c_d}\runtest\par
\vspace{1ex}\def\rawtok{a_b c_d e_f}\runtest\par
\end{document}
答案2
这是一个 Plain TeX(或任何其他格式)的通用宏\scanthechars
,它接受 catcode(1)的字符标记字符串输入12
,并将它们按照相同的顺序但使用转换后的 catcode 放在名为 的标记列表寄存器中\replacetoks
。
(1)此更新\string
为宏添加了两个\@@scanthechars
,因此输入不再仅限于 catcode12
标记。它甚至可能包含控制序列(但不是支撑材料),这些控制序列将以未武装的方式通过,并且可以在各种 catcode 机制之间来回转换 - 请参阅更新后的图像。
指定 catcodes 的接口是通过一个宏来实现的\replacesetup
,该宏通过逗号分隔的列表来获取,这些列表包括\!{3}
将字符标记转换!
为 catcode3
字符标记等内容!
。
说明性测试\detokenize
出于简单目的使用,以便轻松生成 catcode12
标记或逐字输出,因此代码需要etex
或pdftex
用于编译。但宏本身是严格的 Knuthian。
仅处理3
、4
、6
、7
、8
、11
的catcode 12
。13
每次使用\scanthechars
resets \replacesetup
:下次调用 后都\scanthechars
必须重新执行\replacesetup
。由于现在输入不限于只使用 catcode 12
标记,因此可以\scanthechars
对同一字符串使用多次。请参阅代码和图像以了解如何执行此操作。
代码:
\catcode`@ 11
\long\def\@gobble #1{}
\long\def\@firstoftwo #1#2{#1}
\long\def\@secondoftwo #1#2{#2}
\newtoks\replacetoks
\def\replacesetup #1{%
% this assumes non nil escapechar
\def\replace@list {}%
\def\replace@do ##1##2%
{\expandafter\def\csname replace@setup@\expandafter
\@gobble\string##1\endcsname {##2}}%
\replace@setup #1,\relax\relax,%
}
\def\replace@setup #1#2,{%
\ifx#1\relax\replace@list
\else
\expandafter\def\expandafter\replace@list\expandafter
{\replace@list \replace@do #1{#2}}%
\expandafter\replace@setup
\fi
}
\def\scanthechars #1{\replacetoks{}%
\edef\replace@restore{\lccode`$=\the\lccode`$
\lccode`^=\the\lccode`^
\lccode`_=\the\lccode`_
\lccode`&=\the\lccode`&
\lccode`\noexpand\#=\the\lccode`\#
\lccode`a=`a \lccode`(=\the\lccode`(
\lccode`\noexpand~=\the\lccode`~\relax
}%
\@scanthechars #1\relax}
\def\@scanthechars #1{\ifx #1\relax\expandafter\@firstoftwo
\else\expandafter\@secondoftwo
\fi
{\def\replace@do ##1##2{\expandafter
\let\csname replace@setup@\expandafter
\@gobble\string##1\endcsname\relax}%
\replace@list % resets to relax everything
\replace@restore}%
{\@@scanthechars #1}%
}
\def\@@scanthechars #1{\expandafter
\ifx\csname replace@setup@\string#1\endcsname\relax
\replacetoks\expandafter{\the\replacetoks #1}%
\else
\ifcase\csname replace@setup@\string#1\endcsname\relax
\or\or\or % catcode=3
\lccode`$=`#1 %$
\lowercase
{\replacetoks\expandafter{\the\replacetoks $}}%$
\or % catcode=4
\lccode`&=`#1
\lowercase
{\replacetoks\expandafter{\the\replacetoks &}}%
\or\or % 6
\lccode`\#=`#1
\lowercase
{\replacetoks\expandafter{\the\replacetoks ##}}%
\or % 7
\lccode`^=`#1
\lowercase
{\replacetoks\expandafter{\the\replacetoks ^}}%
\or % 8
\lccode`_=`#1
\lowercase
{\replacetoks\expandafter{\the\replacetoks _}}%
\or\or\or % 11
\lccode`a=`#1
\lowercase
{\replacetoks\expandafter{\the\replacetoks a}}%
\or % 12
\lccode`(=`#1
\lowercase
{\replacetoks\expandafter{\the\replacetoks (}}%
\or % 13
\lccode`~=`#1
\lowercase
{\replacetoks\expandafter{\the\replacetoks ~}}%
\fi
\fi
\@scanthechars
}
\catcode`@ 12
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% utility macro to get the catcode of a character token
% manages only catcodes 3,4,6,7,8,11,12,13
\def\TheCatcode #1{\ifcat\noexpand#1$3\else% $
\ifcat\noexpand#1&4\else
\ifcat\noexpand#1##6\else
\ifcat\noexpand#1^7\else
\ifcat\noexpand#1_8\else
\ifcat\noexpand#1a11\else
\ifcat\noexpand#1(12\else
\ifcat\noexpand#1\noexpand~13\else not handled
\fi\fi\fi\fi\fi\fi\fi\fi }
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\tt
Test I.
\replacesetup{\#{3},\!{7},\?{8}}
\detokenize{\replacesetup{\#{3},\!{7},\?{8}}}
\edef\x{\string#a!b?c\string#}
\x
\expandafter\scanthechars\expandafter{\x}
\the\replacetoks
\begingroup
\catcode`#=3
\catcode`!=7
\catcode`?=8
\gdef\z{#a!b?c#}
\endgroup
\the\replacetoks
=\z
\edef\y{\the\replacetoks}
\ifx\z\y OK\else WRONG\fi
\bigskip
Test II.
\replacesetup{\a{11},\b{11},\c{11},\\{3}}
\detokenize{\replacesetup{\a{11},\b{11},\c{11},\\{3}}}
\catcode`@ 11
\edef\backslashchar{\expandafter\@gobble\string\\}
\catcode`@ 12
% creates an x with catcode 12 a, b, c
\edef\x{\string\abc\backslashchar}
\x
\expandafter\scanthechars\expandafter{\x}
\edef\y{\the\replacetoks}
\begingroup
\catcode`|=0
\catcode`\\=3
|gdef|z{\abc\}
|endgroup
\y=\z
\ifx\z\y OK\else WRONG\fi
\bigskip
Test III.
\replacesetup{\a{3},\b{7},\c{8},\d{12}}
\detokenize{\replacesetup{\a{3},\b{7},\c{8},\d{12}}}
\expandafter\scanthechars\expandafter {\detokenize{abcd}}% convert to catcode 12
\edef\y {\the\replacetoks}
\def\PrintTheCatcode #1{\string#1 with catcode \TheCatcode #1\par }
% previously, we used xinttools.sty:
% \xintApplyInline{\PrintTheCatcode}{\y}
\def\PrintTheCatcodes #1{\ifx#1\relax\else\PrintTheCatcode#1\expandafter
\PrintTheCatcodes\fi }
\expandafter\PrintTheCatcodes\y\relax
% \replacesetup must be redone each time
\replacesetup{\a{3},\b{7},\c{8},\d{12}}
\expandafter\scanthechars\expandafter {\detokenize{adbdcda}}
\detokenize{adbdcda}->\the\replacetoks
\bigskip
Test IV.
\overfullrule 0pt
\replacesetup {\!{6}}
\scanthechars {!}
\expandafter\meaning\the\replacetoks
\edef\y{\the\replacetoks}
\begingroup
\catcode`!6
\gdef\z{!!}
\endgroup
\ifx\z\y OK\else WRONG\fi
\bigskip
TEST V.
From now on we don't require the input to be with catcode 12 tokens.
\replacesetup{\#{3}, \_{11}, \^{11}, \!{4}, \?{7}, \&{8}, \@{6}}
\detokenize{\replacesetup{\#{3}, \_{11}, \^{11}, \!{4}, \?{7}, \&{8}, \@{6}}}
\scanthechars {\tabskip1ex #\mathstrut @#\hfil!\hfil #@#\hfil!\hfil#@#\cr
\noalign\bgroup\hrule\egroup
1!23!456\cr 7890!a!bc\cr
\noalign\bgroup\hrule\egroup }
\detokenize{\scanthechars {\tabskip1ex #\mathstrut @#\hfil!\hfil #@#\hfil!\hfil#@#\cr
\noalign\bgroup\hrule\egroup
1!23!456\cr 7890!a!bc\cr
\noalign\bgroup\hrule\egroup }
}
\detokenize{\halign{\span\the\replacetoks}}
output:
\medskip
\halign{\span\the\replacetoks}
% \expandafter\PrintTheCatcodes\the\replacetoks\relax
% attention _ is mathematically active!
% in plain.tex \mathcode`\_="8000 % \_
\bigskip
\replacesetup{\#{3}, \_{11}, \^{11}, \!{4}, \?{7}, \&{8}, \@{6}}
\detokenize {\replacesetup{\#{3}, \_{11}, \^{11}, \!{4}, \?{7}, \&{8}, \@{6}}}
\scanthechars {##___^?\bgroup abc\egroup &\bgroup\hbox\bgroup___\egroup\egroup##}
\detokenize {\scanthechars {##___^?\bgroup abc\egroup &\bgroup\hbox\bgroup___\egroup\egroup##}}
\detokenize{\hrule\the\replacetoks\hrule}
output:
\hrule\the\replacetoks\hrule
\medskip
\string_ is mathematically active, the subscript is in an \string\hbox\space
hence the catcode 11 version is used, but the first three use \string\_ as
defined in plain.tex for the math active \string_, which now has catcode letter
\bigskip
Let's now go back to catcode 12:
\edef\y{\the\replacetoks}
\replacesetup {\#{12}, \_{12}, \^{12}, \!{12}, \?{12}, \&{12}, \@{12}}
\expandafter\scanthechars\expandafter{\y}
\detokenize{\edef\y{\the\replacetoks}}
\detokenize{\replacesetup {\#{12}, \_{12}, \^{12}, \!{12}, \?{12}, \&{12}, \@{12}}}
\detokenize{\expandafter\scanthechars\expandafter{\y}}
\detokenize{\the\replacetoks}
\the\replacetoks
\edef\z{\the\replacetoks}
\detokenize{\edef\z{\the\replacetoks}\meaning\z}
\meaning\z
\nopagenumbers
\bye
答案3
在我发表其他回答之后的几年里,我编写了一个tokcycle
包,用于一次一个标记地获取参数或输入流,并“用它做一些事情”。它可以轻松地设置为对 cat-12#
标记执行某些操作并将其重新转换为 cat-6,因为这似乎是 OP 想要的。
MWE 的工作过程如下:
标准 cat-6 方法
\def
如果参数
\def
包含 cat-12会发生什么情况#
。如果参数和
\def
参数本身都是 cat-12会发生什么情况#
。现在我设置
tokcycle
将 cat-12#
标记转换回 cat-6。在此步骤中,我首先仅将 cat-6#
标记传递给循环,以表明它可以毫无问题地处理它们(但不需要转换)。#
在这里,我将参数中的cat-12 转换\def
回 cat-6,并得到预期结果。#
在这里,我将参数和参数规范中的cat-12 标记转换\def
回 cat-6,并得到预期结果。在这里,我在底层 cat-12
#
环境中操作,并且仍然成功地将出现的事件转换#
回 cat-6,并获得了预期的结果。
妇女权利委员会:
\documentclass {article}
\usepackage[T1]{fontenc}
\usepackage{tokcycle}
\let\SIXhash#
\catcode`#=12
\let\TWELVEhash#
\catcode`#=6
\begin{document}
1.\def\Q#1{My argument is [#1]}
\Q{X}
2.\def\QQ#1{My argument is [\TWELVEhash1]}
\QQ{X}
3.\def\QQQ\TWELVEhash1{My argument is [\TWELVEhash1]}
\QQQ\TWELVEhash1
\Characterdirective{%
\whennotprocessingparameter#1{\tctestifx{\TWELVEhash#1}%
{\addcytoks{##}}{\addcytoks{#1}}}}
4.\tokencyclexpress
\gdef\W#1{My argument is [#1]}
\endtokencyclexpress
\W{X}
5.\tokencyclexpress
\gdef\WW#1{My argument is [\TWELVEhash1]}
\endtokencyclexpress
\WW{X}
6.\tokencyclexpress
\gdef\WWW\TWELVEhash1{My argument is [\TWELVEhash1]}
\endtokencyclexpress
\WWW{X}
7.\catcode`#=12
\tokencyclexpress
\gdef\WWWW#1{My argument is [#1]}
\endtokencyclexpress
\WWWW{X} #####
\end{document}
补充
上面的 cat-6 更改情况更具挑战性,因为 cat-6 标记的处理方式(需要##
、####
等)。对于其他 catcode 转换,tokcycle 过程更简单,因为我不必确定标记是否是参数的一部分。在这里,我对下划线做了类似的事情_
。
\documentclass {article}
\usepackage[T1]{fontenc}
\usepackage{tokcycle}
\catcode`_=12
\let\TWELVEus_
\catcode`_=8
\begin{document}
1.\def\Q{$a\TWELVEus1$}
\Q
\Characterdirective{%
\tctestifx{\TWELVEus#1}{\addcytoks{_}}{\addcytoks{#1}}}
2. \expandafter\tokencyclexpress\Q\endtokencyclexpress
3.\catcode`_=12
\def\Q{$a_1$}
\expandafter\tokencyclexpress\Q\endtokencyclexpress
4. $\tokencyclexpress a_1\endtokencyclexpress$
____
\end{document}