我仍在(再次)尝试不屈服于 TeX 语法。
我想创建一个“if/when”来检查一个字符是否是字母(catcode 11)。
\def\whenletter#1{\expandafter\expandafter\expandafter\ifnum\getcatcode#1=11}
% I'm not confortable to use many \expandafter's in line. I would check if that would make sense.
% the issue is around the \getcatcode
\def\getcatcode#1{\the\catcode`\#1} %fails: \# is the # itself
\def\getcatcode#1{\the\catcode`\\#1} %fails: \\ has no useful meaning in this case
\def\getcatcode#1{\the\catcode`\{{##1}}} %fails: \{ generates a syntax error
\def\getcatcode#1{\the\catcode`\\{{##1}}} %fails: kill my self!
对于专家来说,这很简单,但即使经过多次尝试,我还是无法正确定义 \getcatcode。我想保持易读性。
我的主要目的是更好地理解 TeX 语法及其可能性。
是否可以按这种方式编写 \getcatcode?如果可以,该怎么做?
提前致谢!
答案1
\charcode
未定义。
我想你希望使用\catcode
。
\if
永远不要给宏起一个以开头的名字\ifletter
。
你很快就会发现,你无法将这样的宏与 TeX 的\if...
-primitives 和根据 定义的东西区分开来\newif
。
但是,一边是宏的实例,另一边是\if..
-primitives/ \newif
-thingies 的实例,需要相互区分,因为当使用-branching 的 TeX 进行-matching时,会考虑TeX 的\if...
-primitives 和-thingies 的实例,而宏的实例则不会被考虑。 所以,如果你不遵守只给-primitives/ -thingies 起以 开头的名字的惯例,你可能会很容易对 TeX 考虑/不考虑什么感到困惑\newif
\if..\else..\fi
\if..\else..\fi
\if..
\newif
\if..
\if..\else..\fi
。
如果你真的想知道某个特定字符当前是否被分配了类别代码 11(这实际上是关于 TeX 读取和标记设备行为的信息,而不是关于已存在标记的属性的信息),以便 TeX 将来在从 .tex 输入文件读取时遇到该字符的实例并从刚读取的字符创建标记时,创建一个类别为 11(字母)的明确字符标记,你可以通过\ifnum\the\catcode`#1=11 ... \else ... \fi
以下方式实现#1
常量(即,单字母控制序列或显式字符标记)来实现。
但是,这种检查只能告诉您有关当前读取和标记设备如何调整的一些信息,即在读取和标记 .tex 输入文件时,通过标记所讨论字符的后续实例而产生的显式字符标记将具有的类别。
此检查不会告诉您有关已存在的字符标记的属性的信息。此检查不会告诉您已标记的字符标记属于哪个类别。
为了找出后者,您可以使用\ifcat
。
如果您希望测试已经标记的显式字符标记是否属于类别 11(字母),并且您可以依赖 TeX 的类别代码默认设置,从而可以依赖分配给字符的类别代码 11(字母),A
以便 .tex 输入文件中的字符 A 将被标记化为类别 11(字母)的显式字符标记,您可以执行以下操作:
\long\def\firstoftwo#1#2{#1}%
\long\def\secondoftwo#1#2{#2}%
\def\CheckWhetherTokenOfletterCategory#1{%
\ifcat A\noexpand#1\expandafter\firstoftwo\else\expandafter\secondoftwo\fi
}%
\newlinechar=`\^^J
\message{^^JThe token + \CheckWhetherTokenOfletterCategory{+}{is}{is not} of category 11(letter).}
\message{^^JThe token $ \CheckWhetherTokenOfletterCategory{$}{is}{is not} of category 11(letter).}
\message{^^JThe token A \CheckWhetherTokenOfletterCategory{A}{is}{is not} of category 11(letter).}
\bye
(当谈到分叉/分支时,我经常会使用\expandafter\firstoftwo
/ \expandafter\secondoftwo
-trick,因为这样用户提供的参数就不会出现在\if..\else..\fi
-expression 中。如果用户提供的参数包含不平衡的或-token等,则出现在\if..\else..\fi
-expression 中的用户提供的参数可能会导致正确匹配的问题。)\if..\else..\fi
\if..
\else
\fi
如果由于某种原因您不能依赖 TeX 的类别代码默认设置,从而不能依赖分配给字符 A 的类别代码 11(字母),从而不能依赖于在对输入字符A
进行类别 11(字母)标记时产生的显式字符标记,您可以在让 TeX 从 .tex 文件中读取参数并对其进行标记之前,在本地范围内调整类别代码机制:
\begingroup
% Within the local scope/group do adjustments to the reading- and
% tokenizing-apparatus, i.e., adjustments to the category-code-régime,
% adjustments to the parameter `\endlinechar`:
\def\firstofone#1{#1}%
\catcode`\A=11 %<-Just to make sure...
\firstofone{% \firstofone ensures tokenization of its argument while
% catcode-changes etc introduced within the local scope/
% group are in effect. Then in the gullet the
% replacement of \firstofone , i.e., the set of tokens
% that forms the argument, is delivered. It contains the
% token \endgroup which makes it into the stomach and
% ends the group so that catcode-changes etc are not in
% effect any more when tokenizing stuff that follows
% \firstofone's argument.
\endgroup
\long\def\firstoftwo#1#2{#1}%
\long\def\secondoftwo#1#2{#2}%
\def\CheckWhetherTokenOfletterCategory#1{%
\ifcat A\noexpand#1\expandafter\firstoftwo\else\expandafter\secondoftwo\fi
}%
}%
\newlinechar=`\^^J
\message{^^JThe token + \CheckWhetherTokenOfletterCategory{+}{is}{is not} of category 11(letter).}
\message{^^JThe token $ \CheckWhetherTokenOfletterCategory{$}{is}{is not} of category 11(letter).}
\message{^^JThe token A \CheckWhetherTokenOfletterCategory{A}{is}{is not} of category 11(letter).}
\bye
控制台输出:
The token + is not of category 11(letter).
The token $ is not of category 11(letter).
The token A is of category 11(letter).
如果您真的需要知道在读取和标记 .tex 输入文件时由于标记所讨论字符的后续实例而产生的显式字符标记将属于哪个类别,我建议执行以下操作:
\long\def\firstoftwo#1#2{#1}%
\long\def\secondoftwo#1#2{#2}%
\def\getCurrentCategoryCode#1{\the\catcode`#1}
\def\AtIfSubsequentlyTokenizedCharactersWillHaveCategoryLetter#1{%
\ifnum\getCurrentCategoryCode{#1}=11 %
\expandafter\firstoftwo\else\expandafter\secondoftwo\fi
}%
% Without \getcatcode:
%
%\def\AtIfSubsequentlyTokenizedCharactersWillHaveCategoryLetter#1{%
% \ifnum\the\catcode`#1=11 %
% \expandafter\firstoftwo\else\expandafter\secondoftwo\fi
%}%
\newlinechar=`\^^J
\message{^^J\getCurrentCategoryCode{#}}
\message{^^J\getCurrentCategoryCode{-}}
\message{^^J\getCurrentCategoryCode{+}}
\message{^^J\getCurrentCategoryCode{\%}}
\message{^^J\getCurrentCategoryCode{\\}}
\message{^^J\getCurrentCategoryCode{\{}}
\message{^^J\getCurrentCategoryCode{\}}}
\message{^^J\getCurrentCategoryCode{A}}
\message{^^JSubsequent instances of the hash-character occurring in the .tex-input-file will
\AtIfSubsequentlyTokenizedCharactersWillHaveCategoryLetter{#}{be}{not be} tokenized as explicit character tokens of letter category.}
\message{^^JSubsequent instances of the character - occurring in the .tex-input-file will
\AtIfSubsequentlyTokenizedCharactersWillHaveCategoryLetter{-}{be}{not be} tokenized as explicit character tokens of letter category.}
\message{^^JSubsequent instances of the character + occurring in the .tex-input-file will
\AtIfSubsequentlyTokenizedCharactersWillHaveCategoryLetter{+}{be}{not be} tokenized as explicit character tokens of letter category.}
\message{^^JSubsequent instances of the percent-character occurring in the .tex-input-file will
\AtIfSubsequentlyTokenizedCharactersWillHaveCategoryLetter{\%}{be}{not be} tokenized as explicit character tokens of letter category.}
\message{^^JSubsequent instances of the backslash character occurring in the .tex-input-file will
\AtIfSubsequentlyTokenizedCharactersWillHaveCategoryLetter{\\}{be}{not be} tokenized as explicit character tokens of letter category.}
\message{^^JSubsequent instances of the curly left brace occurring in the .tex-input-file will
\AtIfSubsequentlyTokenizedCharactersWillHaveCategoryLetter{\{}{be}{not be} tokenized as explicit character tokens of letter category.}
\message{^^JSubsequent instances of the curly right brace occurring in the .tex-input-file will
\AtIfSubsequentlyTokenizedCharactersWillHaveCategoryLetter{\}}{be}{not be} tokenized as explicit character tokens of letter category.}
\message{^^JSubsequent instances of the character A occurring in the .tex-input-file will
\AtIfSubsequentlyTokenizedCharactersWillHaveCategoryLetter{A}{be}{not be} tokenized as explicit character tokens of letter category.}
\bye
控制台输出:
6
12
12
14
0
1
2
11
Subsequent instances of the hash-character occurring in the .tex-input-file wil
l not be tokenized as explicit character tokens of letter category.
Subsequent instances of the character - occurring in the .tex-input-file will n
ot be tokenized as explicit character tokens of letter category.
Subsequent instances of the character + occurring in the .tex-input-file will n
ot be tokenized as explicit character tokens of letter category.
Subsequent instances of the percent-character occurring in the .tex-input-file
will not be tokenized as explicit character tokens of letter category.
Subsequent instances of the backslash character occurring in the .tex-input-fil
e will not be tokenized as explicit character tokens of letter category.
Subsequent instances of the curly left brace occurring in the .tex-input-file w
ill not be tokenized as explicit character tokens of letter category.
Subsequent instances of the curly right brace occurring in the .tex-input-file
will not be tokenized as explicit character tokens of letter category.
Subsequent instances of the character A occurring in the .tex-input-file will b
e tokenized as explicit character tokens of letter category.
答案2
{<more than one token>}
假设您要测试下一个非空格标记是否为字母,即类别代码为 11。这对于after不起作用\isletter
。
\newlinechar=`^^J % for newlines
\def\isletter#1{%
TT\fi
\ifcat\noexpand#1\relax
% the next token is a control sequence
\expandafter\testletchar
\else
\expandafter\testchar
\fi
#1%
}
\def\testletchar#1{%
\ifcat\noexpand#1a%
}
\def\testchar#1{%
\ifcat\noexpand#1a% \noexpand for active characters
}
\let\achar=a
\def\test#1{
\if\isletter #1%
\message{LETTER^^J}%
\else
\message{NONLETTER^^J}%
\fi
}
\test{a}
\test{\achar}
\test{~}
\test{\hfuzz}
\test{$}
\test{b}
\bye
控制台上的输出是
a = LETTER
\achar = LETTER
~ = NONLETTER
\hfuzz = NONLETTER
$ = NONLETTER
b = LETTER
测试\ifnum
会失败\achar
。
如果您还想检查大括号,则需要使用更复杂的方法\futurelet
。