如何确定包含具有可选参数的宏的数组的大小?

如何确定包含具有可选参数的宏的数组的大小?

我正在使用我的解决方案如何确定数组的大小?计算条目数。但是,当条目是宏(在 MWE 中)且带有可选参数时, \foreach/\IfStrEq似乎\SetArrayLength存在问题。我天真地以为只需在本地重新定义宏即可轻松修复此问题:\Ellipsis

\renewcommand{\Ellipsis}[1][]{z}%

但这仍然不允许我计算\ListBnor的条目数\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
}

相关内容