如何将宏的扩展分配给 \count?

如何将宏的扩展分配给 \count?

\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可能不是您想要在那里写的内容),但无论如何我都会避免修改不相关的部分,因此很容易与原始尝试进行比较。

相关内容