是否可以计算 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
基本上,下一个标记是\let
to \@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}