我正在使用我的解决方案如何确定数组的大小?计算条目数。但是,当条目是宏(在 MWE 中)且带有可选参数时, \foreach
/\IfStrEq
似乎\SetArrayLength
存在问题。我天真地以为只需在本地重新定义宏即可轻松修复此问题:\Ellipsis
\renewcommand{\Ellipsis}[1][]{z}%
但这仍然不允许我计算\ListB
nor的条目数\ListC
。结果错误消息是
Undefined control sequence. \@xs@IfStrEq@@ ...\@xs@arg@ii {#2}\edef \@xs@call {\noexpand \@xs@IfStrEq {\...
笔记:
- 这是我所拥有的一个大大简化的版本,因此很多实用性在这里都丢失了。
代码:
\documentclass{article}
\usepackage{tikz}
\usepackage{xstring}
\usepackage{xparse}
\usepackage{xcolor}
%%% https://tex.stackexchange.com/questions/100542/how-to-extract-the-name-of-a-macro
\ExplSyntaxOn
\newcommand{\CsToStr}[1]{\cs_to_str:N #1}
\ExplSyntaxOff
\makeatletter
\newcounter{@LengthOfArray}%
\newcommand{\GetArrayLength}{\arabic{@LengthOfArray}}%
\newcommand*{\SetArrayLength}[1]{%
% https://tex.stackexchange.com/questions/66121/how-can-i-determine-the-size-of-an-array
%
% Counts the number of array members. This ignore any empty
% array members created by extraneous commas (such as those
% as result of a double comma, or a trailing comma.
%
\setcounter{@LengthOfArray}{0}%
\begingroup
\renewcommand{\Ellipsis}[1][]{z}% <-- Redefine locally for counting
\foreach \x in #1 {%
\IfStrEq{\x}{}{}{%% only increment when non-empty
\stepcounter{@LengthOfArray}%
}%
}%
\endgroup
}%
\newcommand*{\Ellipsis}[1][\dots]{%
\,#1\ltx@ifnextchar{,}{\,}{}%
}%
\makeatother
\newcommand*{\ListA}{1,2,3,,,,4,}
\newcommand*{\ListB}{1,2,3,,,,4,\Ellipsis}
\newcommand*{\ListC}{1,2,3,,,,4,\Ellipsis,5}
\newcommand*{\CheckLength}[2]{%
% #1 = string macro
% #2 = expected length
\SetArrayLength{#1}%
Length of \CsToStr{#1} is \GetArrayLength%
\ifnum\GetArrayLength=#2\relax
~as expected.
\else
, \textcolor{red}{but should have been #2!!}
\fi
}%
\begin{document}
%Test \verb|\Ellipsis:|
%$1, \Ellipsis.$ \quad
%$1, \Ellipsis[\cdots], 2$
%\bigskip
\CheckLength{\ListA}{4}\par %% This is ok, but following two have issues.
\CheckLength{\ListB}{5}\par
\CheckLength{\ListC}{6}
\end{document}
答案1
xstring
我想说的是, 的扩展问题很常见。但你把事情复杂化了。\clist_set:Nn
的功能是expl3
清除空项目,所以它是一个简单的应用程序。
\documentclass{article}
\usepackage{xparse}
\usepackage{xcolor}
\ExplSyntaxOn
\NewDocumentCommand{\SetArrayLength}{sm}
{
\IfBooleanTF{#1}
{% star form: expect a control sequence
\grill_array_length:V #2
}
{
\grill_array_length:n { #2 }
}
}
\DeclareExpandableDocumentCommand{\GetArrayLength}{}
{
\clist_count:N \l_grill_purged_array_clist
}
\clist_new:N \l_grill_purged_array_clist
\cs_new_protected:Nn \grill_array_length:n
{
\clist_set:Nn \l_grill_purged_array_clist { #1 }
}
\cs_generate_variant:Nn \grill_array_length:n { V }
\ExplSyntaxOff
\makeatletter
\newcommand*{\Ellipsis}[1][\dots]{%
\,#1\ltx@ifnextchar{,}{\,}{}%
}
\makeatother
\newcommand*{\ListA}{1,2,3,,,,4,}
\newcommand*{\ListB}{1,2,3,,,,4,\Ellipsis}
\newcommand*{\ListC}{1,2,3,,,,4,\Ellipsis,5}
\newcommand*{\CheckLength}[2]{%
% #1 = string macro
% #2 = expected length
\SetArrayLength*{#1}%
Length of \texttt{\string#1} is \GetArrayLength
\ifnum\GetArrayLength=#2\relax
~as expected.
\else
, \textcolor{red}{but should have been #2!!}
\fi
}%
\begin{document}
%Test \verb|\Ellipsis:|
%$1, \Ellipsis.$ \quad
%$1, \Ellipsis[\cdots], 2$
%\bigskip
\CheckLength{\ListA}{4}\par %% This is ok, but following two have issues.
\CheckLength{\ListB}{5}\par
\CheckLength{\ListC}{6}
\end{document}
你可以做
\let\Ellipsis\relax
而不是\renewcommand{\Ellipsis}[1][]{z}
,但是使用上述策略,您不必关心列表中有什么项目。
正确的策略xstring
是\noexpandarg
:
\newcommand*{\SetArrayLength}[1]{%
% http://tex.stackexchange.com/questions/66121/how-can-i-determine-the-size-of-an-array
%
% Counts the number of array members. This ignore any empty
% array members created by extraneous commas (such as those
% as result of a double comma, or a trailing comma.
%
\setcounter{@LengthOfArray}{0}%
\begingroup
\noexpandarg
\foreach \x in #1 {%
\expandafter\IfStrEq\expandafter{\x}{}{}{%% only increment when non-empty
\stepcounter{@LengthOfArray}%
}%
}%
\endgroup
}