\findtoken
是一个宏,它在预定义列表(“a”、“b”、“c”、“d”)中查找其参数(完整展开)的索引(从零开始)#1
;如果此展开不在列表中,它将停止编译。
似乎可以很好地排版此索引,但我希望能够将这个数字分配给\count
,例如\newcount\mycount \mycount=\findtoken{c}\relax \the\mycount
排版“2”。
我在作业中尝试了很多组合\expandafter
,也读过为什么 TeX 扫描器对寄存器编号和宏名的标记处理方式不同?
和如何编写一个接受数字或 \count 寄存器作为参数的 TeX 宏?,但却想不出办法做到这一点。
那么,可能吗?
\documentclass[margin=2mm]{standalone}
\usepackage[T1]{fontenc}
\makeatletter
\newcount\mycount
\newcommand*{\findtoken}[1]{%
% input (#1): a b c d <anything else>
% output: 0 1 2 3 error
\begingroup % protect scratch macros
\edef\@tempa{\expanded{#1}}%
\@tempswafalse % did anything match?
\@tempcnta=\z@ % index of the list
% loop
\@for\@tempb:={a,b,c,d}\do{%
% if they match, record the current index
\ifx\@tempb\@tempa
\@tempswatrue
\expandafter\aftergroup\the\@tempcnta
\fi
\advance\@tempcnta\@ne
}% end of loop
\if@tempswa\else
\STOPCOMPILATION
\fi
\endgroup
}
\makeatother
\begin{document}
\def\lettera{a}
Checking whether \string\findtoken\ works:
\findtoken{\lettera},
\findtoken{a},
\findtoken{b},
\findtoken{c},
\findtoken{d}.
% uncomment the next line to stop compilation:
% \findtoken{x}
% now, trying to assign \findtoken's expansion into \mycount:
% \mycount\findtoken{c}\relax
% ^ what to write above?! ^
\string\mycount's value is: \the\mycount.
\end{document}
答案1
其他答案提到了核心:如果在语法中使用宏,\register=\macro
则必须扩张到宏值,因为赋值是在扩展之后进行的。这里不能设置不可扩展的控制序列(如\begingroup
或)。\edef
我添加了一个非常简短的可扩展宏替代方案示例。TeX 是一种非常紧凑的语言。
\newcount\mycount
\def\findtoken #1{%
\ifcase\numexpr \expandafter`#1-`a\relax
0\or 1\or 2\or 3\else \STOPCOMPILATION \fi
}
\def\lettera{a}
Checking whether \string\findtoken\ works:
\findtoken{\lettera},
\findtoken{a},
\findtoken{b},
\findtoken{c},
\findtoken{d}.
% uncomment the next line to stop compilation:
% \findtoken{x}
% now, trying to assign \findtoken's expansion into \mycount:
\mycount=\findtoken{c}\relax
\string\mycount's value is: \the\mycount.
编辑:根据评论,我展示了另一个紧凑且可扩展的解决方案:
\newcount\mycount
\def\declcode#1#2{\expandafter\def\csname findtoken:#1\endcsname{#2}}
\def\findtoken#1{\ifcsname findtoken:#1\endcsname
\csname findtoken:#1\endcsname \else \STOPCOMPILATION \fi
}
\declcode{a}{0}
\declcode{b}{1}
\declcode{c}{2}
\declcode{d}{3}
\def\lettera{a}
Checking whether \string\findtoken\ works:
\findtoken{\lettera},
\findtoken{a},
\findtoken{b},
\findtoken{c},
\findtoken{d}.
% uncomment the next line to stop compilation:
% \findtoken{x}
% now, trying to assign \findtoken's expansion into \mycount:
\mycount=\findtoken{c}\relax
\string\mycount's value is: \the\mycount.
答案2
如果宏(如此处)不是使用扩展编写的,则可以像 xstring 或 pgf 那样工作并将结果留在扩展的宏中:
\documentclass[margin=2mm]{standalone}
\usepackage[T1]{fontenc}
\makeatletter
\newcount\mycount
\newcommand*{\findtoken}[1]{%
% input (#1): a b c d <anything else>
% output: 0 1 2 3 error
\begingroup % protect scratch macros
\edef\@tempa{\expanded{#1}}%
\@tempswafalse % did anything match?
\@tempcnta=\z@ % index of the list
% loop
\xdef\findtokenresult{}%
\@for\@tempb:={a,b,c,d}\do{%
% if they match, record the current index
\ifx\@tempb\@tempa
\@tempswatrue
% \expandafter\aftergroup\the\@tempcnta
\xdef\findtokenresult{\the\@tempcnta}%
\fi
\advance\@tempcnta\@ne
}% end of loop
\if@tempswa\else
\STOPCOMPILATION
\fi
\endgroup
}
\makeatother
\begin{document}
\def\lettera{a}
Checking whether \string\findtoken\ works:
\findtoken{\lettera}\findtokenresult,
\findtoken{a}\findtokenresult,
\findtoken{b}\findtokenresult,
\findtoken{c}\findtokenresult,
\findtoken{d}\findtokenresult.
% uncomment the next line to stop compilation:
% \findtoken{x}
% now, trying to assign \findtoken's expansion into \mycount:
\findtoken{c}\mycount\findtokenresult\relax
% ^ what to write above?! ^
\string\mycount's value is: \the\mycount.
\end{document}
答案3
使用可扩展的实现。
\documentclass{article}
\usepackage[T1]{fontenc}
\ExplSyntaxOn
\NewExpandableDocumentCommand{\findtoken}{m}
{
\str_case_e:nnF { #1 }
{
{a}{0}
{b}{1}
{c}{2}
{d}{3}
}
{\errorstopmode\ERROR}
}
\ExplSyntaxOff
\newcount\mycount
\begin{document}
\def\lettera{a}
Checking whether \string\findtoken\ works:
\findtoken{\lettera},
\findtoken{a},
\findtoken{b},
\findtoken{c},
\findtoken{d}.
% uncomment the next line to stop compilation:
% \findtoken{x}
% now, trying to assign \findtoken's expansion into \mycount:
\mycount\findtoken{c}\relax
\string\mycount's value is: \the\mycount.
\end{document}
答案4
有可能嗎?有可能。
如果坚持使用
\mycount\findtoken{a}
语法,是否可行?不行。(除非你重写\findtoken
宏,使其完全可扩展)请参阅下面的代码。
%! TEX program = pdflatex \documentclass[margin=2mm]{standalone} \usepackage[T1]{fontenc} \makeatletter \newcount\mycount \newcommand*{\findtokenx}[2]{% % input (#1): a b c d <anything else> % input (#2): a single token to be placed immediately before the value % output: 0 1 2 3 error \begingroup % protect scratch macros \aftergroup#2% \edef\@tempa{\expanded{#1}}% \@tempswafalse % did anything match? \@tempcnta=\z@ % index of the list % loop \@for\@tempb:={a,b,c,d}\do{% % if they match, record the current index \ifx\@tempb\@tempa \@tempswatrue \expandafter\aftergroup\the\@tempcnta \fi \advance\@tempcnta\@ne }% end of loop \if@tempswa\else \STOPCOMPILATION \fi \endgroup } \newcommand*{\findtoken}[1]{% \findtokenx{#1}\empty } \makeatother \begin{document} \def\lettera{a} Checking whether \string\findtoken\ works: \findtoken{\lettera}, \findtoken{a}, \findtoken{b}, \findtoken{c}, \findtoken{d}. % uncomment the next line to stop compilation: % \findtoken{x} % now, trying to assign \findtoken's expansion into \mycount: \findtokenx{c}\mycount % ^ what to write above?! ^ \string\mycount's value is: \the\mycount. \end{document}
它的工作原理应该比较明显,只需追溯其逻辑即可。
最初的尝试有什么问题?那
\begingroup
是不可扩展的。阅读更多: 何谓“扩张”?,为什么我不能在 <some other macro> 的参数中使用 <some macro>?。
顺便说一句,只有扩展为单个标记(即一位数)\expandafter\aftergroup\the\counta
时才会按预期工作\the\counta
代码中还有更多怪癖(例如\expanded
可能不是您想要在那里写的内容),但无论如何我都会避免修改不相关的部分,因此很容易与原始尝试进行比较。