对于索引,我想编写一个宏\macroname
,从宏名称中删除前导反斜杠,但保留环境名称不变。即
\macroname{\relax} --> relax
\macroname{itemize} --> itemize
这里的-->
应该读作“扩展为”。
据我了解,以下方法应该可行:
\newcommand\removebs[1]{\if#1\char92\else#1\fi}
\newcommand\macroname[1]{%
\expandafter\removebs\detokenize{#1}}
然而其结果是
\macroname{\relax} --> \relax % literally
\macroname{itemize} --> itemize
实际上,我偶然发现,如果我在宏定义期间将反斜杠的 catcode 更改为“other” \removebs
,它就会起作用:
{
\catcode`\|=0
|catcode`|\=12
|global|def|removebs#1{|if#1\|else#1|fi}
}
\newcommand\macroname[1]{%
\expandafter\removebs\detokenize{#1}}
这是为什么呢?TeX 按主题分类我希望\if
Philipp Lehman 所做的比较就是所谓的“类别代码不可知论”。
感谢您的回答!
答案1
Joseph 给出了一个可行的解决方案。我想解释一下你的代码出了什么问题。
第一次尝试
\newcommand\removebs[1]{\if#1\char92\else#1\fi}
\newcommand\macroname[1]{%
\expandafter\removebs\detokenize{#1}}
和\macroname{\relax}
你一起得到
\expandafter\removebs\detokenize{\relax}
然后(使用•
来分隔标记,并<space>
使用 do 来表示空格标记)
\removebs • \ • r • e • l • a • x • <space>
变成
\if • \ • \char • 9 • 2 • \else • \ • \fi • r • e • l • a • x • <space>
并且比较的是类别代码 12 反斜杠和标记\char
,这当然会导致错误。
代码\char92
是指令打印当前字体中的第 92 个字符。
可以通过检查真实类别代码 12 反斜杠来更正代码:
\makeatletter
\newcommand{\removebs}[1]{\if#1\@backslashchar\else#1\fi}
\makeatother
\detokenize
但after产生的空间\relax
将会保留。
第二次尝试
{
\catcode`\|=0
|catcode`|\=12
|global|def|removebs#1{|if#1\|else#1|fi}
}
这是有效的,因为它仅实现了针对类别代码 12 反斜杠的检查,但这不是必需的,因为该标记已\@backslashchar
在 LaTeX 内核中可用。
另一种方法,无需全局定义和类别更改,可以是
\begingroup\lccode`\|=`\\
\lowercase{\endgroup\def\removebs#1{\if#1|\else#1\fi}}
其中唯一转换为其小写等效项的标记是|
(反斜杠)。
建议代码
% \makeatletter
% \newcommand{\removebs}[1]{\if#1\@backslashchar\else#1\fi}
% \makeatother
\begingroup\lccode`\|=`\\
\lowercase{\endgroup\def\removebs#1{\if#1|\else#1\fi}}
\newcommand{\macroname}[1]{\expandafter\removebs\string#1}
\macroname{itemize}
因为您想要的只是获取参数字母,所以第一个字母是否具有类别代码 12并不重要。
答案2
TeX 使用 token,您需要知道的是,诸如 这样的控制序列\foo
不是\if
作为一系列字符进行测试,而是作为单个“单元”进行测试。有一些原语可以将控制序列重新转换为单个 token:\detokenize
对一组 token 执行此操作,而\string
对单个 token 执行此操作。后者在所有 TeX 版本中也可用(\detokenize
需要 e-TeX)。
如您所见,对输入进行去标记化允许您与类别代码 'other' 进行比较\
。另一种方法是测试是否是控制序列,使用将所有(未扩展的)控制序列视为相同的#1
事实。然后,您可以使用转换为多个标记,并删除第一个字符:\ifcat
\string
\@gobble
\documentclass{article}
\makeatletter
\newcommand{\removeabs}[1]{%
\ifcat\relax\noexpand#1%
\expandafter\expandafter\expandafter\@gobble\expandafter\string
\fi
#1%
}
\makeatother
\begin{document}
\removeabs{foo}
\removeabs{\bar}
\end{document}
只要\escapechar
是可打印的并且不是空格,以上内容都可以。使用 LaTeX3 中的一些代码“翻译”回原语,您可以设置所有转义字符的健壮性:
\documentclass{article}
\makeatletter
\newcommand{\removeabs}[1]{%
\ifcat\relax\noexpand#1%
\expandafter\removeabs@aux@i
\fi
#1%
}
\newcommand*{\removeabs@aux@i}{%
\romannumeral
\if\string\ \removeabs@aux@ii\fi
\expandafter\removeabs@aux@iii\string
}
\newcommand{\removeabs@aux@ii}{}
\long\def\removeabs@aux@ii#1\removeabs@aux@iii{%
-\number\fi\expandafter\z@
}
\newcommand{\removeabs@aux@iii}[1]{\z@}
\makeatother
\begin{document}
\removeabs{foo}
\removeabs{\bar}
\end{document}
(请参阅 LaTeX3 文档中的实现,\cs_to_str:N
了解这里发生的情况!)
答案3
我的看法:
\def\cwofcs#1#2 \endcs{\ifnum\the\catcode`#1=0#2\else#1#2\fi}
\def\nameof#1{\expandafter\cwofcs\detokenize{#1}\endcs}
测试:
\nameof\relax % --> relax
\nameof\itemize % --> itemize