计数代币

计数代币

是否可以计算 token/宏参数。例如:

\def\foo#1{...}
$ \foo{\alpha\beta} $ % => \somecounter = 2

答案1

Joseph Wright 的解决方案不需要额外的辅助宏(\@stop\cleanup@nil):

\makeatletter
\def\counttokens#1{%
  \def\counttokens##1{%
    \the\numexpr\expandafter\@counttokens##1#1\@nnil\@nnil\relax
  }%
}
\counttokens{ }
\long\def\@counttokens#1{%
    \csname @\ifx#1\@nnil first\else second\fi oftwo\endcsname
    {0\remove@to@nnil}{1+\@counttokens}
}

\def\test{abc de\par}
\counttokens\test
\counttokens{}

Martin Scharrer 的解决方案可以做得更优雅,如下所示。除其他问题外,不需要宏\eatspace

\makeatletter
\def\counttokens#1{%
  \def\counttokens##1{%
    \let\tokencount\m@ne
      \def\@counttokens{\futurelet\@let@token\@@counttokens}%
      \expandafter\@counttokens##1#1\@nnil
  }%
}
\counttokens{ }
\def\@@counttokens{%
  \csname @\ifx\@let@token\@nnil first\else second\fi oftwo\endcsname
    \remove@to@nnil{%
        \edef\tokencount{\the\numexpr\tokencount+1}%
    \afterassignment\@counttokens
      \let\@let@token= %
    }%
}

\counttokens{A{something}\beta}
\show\tokencount

\counttokens{ 1 2 3 }
\show\tokencount

答案2

取决于你是否要计算空格。如果你不介意跳过空格,那么这相对容易。使用 e-TeX,可以生成一种可扩展的方法:

\catcode`\@=11\relax
\def\counttokens#1{%
  \number\numexpr\expandafter\counttokens@aux@i#1\@stop\@nil0\relax
}
\long\def\counttokens@aux@i#1{%
  \ifx#1\@stop
    \expandafter\cleanup@nil
  \else
    1 +
    \expandafter\counttokens@aux@i
  \fi
}
\long\def\cleanup@nil#1\@nil{}
\def\@stop{\@stop}
\def\test{abc de\par}
\counttokens\test
\bye

(这本质上是expl3\tl_elt_count:N用纯 e-TeX 实现的函数。)

另一种方法是不需要可扩展的解决方案

\newcount\tokencount
\catcode`\@=11\relax
\def\:{\let\@sptoken= } \: %
\def\@stop{\@stop}
\def\counttokens#1{% 
  \begingroup
    \tokencount\z@
    \expandafter\counttoken@aux@i#1\@stop
}
\def\counttoken@aux@i{%
  \futurelet\@let@token\counttoken@aux@ii
}
\def\counttoken@aux@ii{%
  \ifx\@let@token\@stop
    \expandafter\counttoken@end
  \else
    \advance\tokencount\@ne
    \ifx\@let@token\@sptoken
      \expandafter\expandafter\expandafter\counttoken@space
    \else
      \expandafter\expandafter\expandafter\counttoken@std
    \fi
  \fi
}
\def\:{\counttoken@space} \expandafter\def\: {%
  \futurelet\@let@token\counttoken@aux@ii
}
\long\def\counttoken@std#1{%
  \counttoken@aux@i
}
\def\counttoken@end\@stop{%
  \expandafter\endgroup
  \number\tokencount
}
\def\test{abc  de\par}
\counttokens\test
\bye

我没有为s&中的标记添加任何保护代码\halign,而这在实际解决方案中是必需的。(请参阅TeXbook,看看我的意思。)

答案3

是的,使用类似这样的方法:(更新版本还计算了空间标记)

\makeatletter
\newcount\tokencount
\def\counttoken#1{%
   \tokencount\z@ % set it to zero
   \def\x{\futurelet\@let@token\@counttoken}%
   \x#1\end@counttoken
}
% Unique endmarker
\def\end@counttoken{\@gobble{end@counttoken}}
% A macro which eats a space, the \@firstofone is required to
% avoid that the space in the parameter text itself is eaten:
\@firstofone{\def\eatspace} {}
% Recursive Macro => Loop
\def\@counttoken{%
   \ifx\@let@token\end@counttoken
      \let\x\relax
   \else
      \advance\tokencount by 1\relax
   \fi
   \ifx\@let@token\@sptoken
       % The space token will be eaten
       % Note that two spaces will still be combined by TeX!
       % If this is not what is wanted the catcode of space
       % must be changed beforehand, e.g. using \obeyspaces!
       \expandafter\expandafter
       \expandafter\x
       \expandafter\eatspace
   \else
       % Eat the next non-space token now, then call \x
       \afterassignment\x
       \expandafter\let\expandafter\@let@token\expandafter=%
   \fi
}

测试:

\counttoken{A{something}\beta}
\showthe\tokencount
% Prints:> 13
\counttoken{123}
\showthe\tokencount
% Prints:> 3

基本上,下一个标记是\letto \@let@token,之后 ( \afterassignment) 计数标记并对照结束标记进行检查。然后宏调用自身,直到到达结束标记。计数除结束标记之外的所有标记。您应该在正确的位置添加一些分组,这取决于您应用代码的确切方式。

如果你真的只想计算宏参数,那就A{something}B算作 3:

\newcount\argcount
\def\countargs#1{%
   \argcount\@z % set it to zero
   \@countargs#1\end@countargs
}
% Unique endmarker
%\def\end@countargs{\@gobble{end@countargs}} % doesn't really have to be defined; never executed
\def\end@@countargs{\end@countargs}
% Recursive Macro => Loop
\def\@countargs#1{%
   \def\@tempa{#1}%
   \ifx\@tempa\@end@@counttoken\else
      \advance\tokencount by 1\relax
      \expandafter\@countargs
   \fi
}

答案4

Martin 的\countargs宏不会计算参数中的空格,因为宏获取参数时可能会忽略前导空格。如果我们想计算参数中的空格(目前我认为没有必要这样做,但这是一个有趣的场景),那么以下代码应该可以工作:

\documentclass{article}
\usepackage{catoptions}
\makeatletter
\newvariables{count}{}{argcount,spacecount}[\m@ne]
\def\countargs#1{%
  \def\countargs##1{%
    \begingroup
    \def\end@countargs{\end@countargs}%
    \def\countargs@a{\futurelet\next\countargs@c}%
    \def\countargs@b####1{\countargs@a}%
    \expandafter\countargs@a##1#1\end@countargs
  }%
}
\countargs{ }
\def\countargs@c{%
  \ifxTF\next\end@countargs{%
    \edef\next{%
      \endgroup\argcount\the\argcount\relax
      \spacecount\the\spacecount\relax
    }%
    \expandafter\next\@gobble
  }{%
    \advance\argcount\@ne
    \ifxTF\next\@sptoken{%
      \advance\spacecount\@ne
      \afterassignment\countargs@a
     \let\next= %
    }{%
      \countargs@b
    }%
  }%
}

% Test
\def\reserved@a#1{{#1#1#1A#1{something}#1\beta#1}}
\expandafter\countargs\reserved@a{ }
\edef\result{%
  \string\argcount: \the\argcount,\@space
  \string\spacecount: \the\spacecount
}
\show\result

\end{document}

相关内容