答案之一到关于如何计算PGF数组的长度的问题提供了一个函数,该函数为其参数提供的数组上迭代的每个步骤增加一个计数器。
但是,如果我将一个数组存储在一个变量中,\def\myarray{{1,2,3,4,5,6}}
然后使用\arrayLength{\myarray}
,则输出的是数字 1,而不是 6。我相信这与如何扩展有关\myarray
,但不明白是什么。\expandafter\arrayLength{\myarray}
也输出 1。
一个函数如何才能\len
正确输出数组中元素的数量,无论是\len{{1,2,3}}
还是\len{\myarray}
?
一位 MWE 表示:
\documentclass[tikz]{standalone}
\newcounter{arraycard}
\def\arrayLength#1{%
\setcounter{arraycard}{0}%
\foreach \x in #1{%
\stepcounter{arraycard}%
}%
\the\value{arraycard}%
}
\begin{document}
The length of $\{1,2,3\}$ is \arrayLength{{1,2,3}}.
The length of $\{1,2,\{3,4\},5\}$ is \arrayLength{{1,2,{3,4},5}}.
\def\myarray{{1,2,3,4,5,6}}
The length of \verb|\myarray| is \arrayLength{\myarray}.
\end{document}
答案1
你想在计算之前展开宏,所以
\documentclass[tikz]{standalone}
\newcounter{arraycard}
\def\arrayLength#1{\expandafter\xarraylength\expandafter{#1}}%
\def\xarraylength#1{%
\setcounter{arraycard}{0}%
\foreach \x in #1{%
\stepcounter{arraycard}%
}%
\the\value{arraycard}%
}
\begin{document}
The length of is \arrayLength{{\newcounter,\begin,\tikz}}.
\def\myarray{{\newcounter,\begin,\tikz}}
The length of \verb|\myarray| is \arrayLength\myarray.
\end{document}
答案2
其中一个版本\arrayLength
检查参数中的第一个标记是否是第二个左括号,如果是,则检查内部组后面是否没有任何内容,如果是,则计算元素。如果第一个标记不是左括号,则将第一个标记展开一次,再次测试第一个标记是否是左括号,并且内部组后面没有任何内容,如果是,则计算元素。如果参数未通过这些测试,则会抛出错误并返回 0。
该宏是完全可扩展的。
\documentclass[border=3.14]{standalone}
\usepackage{xparse}
\ExplSyntaxOn
\group_begin:
\char_set_catcode_letter:n { 32 }% spaces are letters until the next \group_end:
\cs_new:Npn\__xarrayLength_bad_argument:{\xarrayLength error: no TikZ array}%
\group_end:%
\NewExpandableDocumentCommand \xarrayLength { +m }
{
\tl_if_head_is_group:nTF { #1 }
{ \__xarrayLength_count_first_group:nw #1 \q_stop }
{ \__xarrayLength_aux:o { #1 } }
}
\cs_new:Npx \__xarrayLength_aux:n #1
{
\exp_not:N \tl_if_head_is_group:nTF { #1 }
{ \exp_not:N \__xarrayLength_count_first_group:nw #1 \exp_not:N \q_stop }
{ \exp_after:wN \exp_not:N \__xarrayLength_bad_argument: 0 }
}
\cs_generate_variant:Nn \__xarrayLength_aux:n { o }
\cs_new:Npx \__xarrayLength_count_first_group:nw #1 #2 \q_stop
{
\exp_not:N \tl_if_empty:nTF { #2 }
{ \exp_not:N \clist_count:n { #1 } }
{ \exp_after:wN \exp_not:N \__xarrayLength_bad_argument: 0 }
}
\ExplSyntaxOff
\newcommand\myarray{{1,2,3,4,5,6}}
\newcounter{elements}
\begin{document}
% \setcounter to prove it is expandable
\setcounter{elements}{\xarrayLength{\myarray}}\arabic{elements}
\xarrayLength{{1,2,3,4,5,6}}
\renewcommand\myarray{1,2,3,4,5,6}%
% \setcounter to prove it is expandable
\setcounter{elements}{\xarrayLength{\myarray}}\arabic{elements}
\xarrayLength{1,2,3,4,5,6}
\end{document}
答案3
由于您使用的是 PGF 数组(始终用“双括号”表示),因此expl3
一行代码就足够了:
\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\NewExpandableDocumentCommand{\arrayLength}{m}
{% #1 = explicit or implicit array
\diniz_array_length:o { #1 }
}
\cs_new:Nn \diniz_array_length:n
{
\clist_count:n #1
}
\cs_generate_variant:Nn \diniz_array_length:n { o }
\ExplSyntaxOff
\begin{document}
The length of $\{1,2,3\}$ is \arrayLength{{1,2,3}}.
The length of $\{1,2,\{3,4\},5\}$ is \arrayLength{{1,2,{3,4},5}}.
\def\myarray{{1,2,3,4,5,6}}
The length of \verb|\myarray| is \arrayLength{\myarray}.
\end{document}
如果您想要最后一项的位置,根据 PGF 约定,它是项数减一,因为索引从 0 开始。
\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\NewExpandableDocumentCommand{\arrayLength}{m}
{% #1 = explicit or implicit array
\diniz_array_length:o { #1 }
}
\cs_new:Nn \diniz_array_length:n
{
\int_eval:n { \clist_count:n #1 - 1 }
}
\cs_generate_variant:Nn \diniz_array_length:n { o }
\ExplSyntaxOff
\begin{document}
The length of $\{1,2,3\}$ is \arrayLength{{1,2,3}}.
The length of $\{1,2,\{3,4\},5\}$ is \arrayLength{{1,2,{3,4},5}}.
\def\myarray{{1,2,3,4,5,6}}
The length of \verb|\myarray| is \arrayLength{\myarray}.
\end{document}
这里的答案是 2、3 和 5。
诀窍在于,如果您调用\arrayLength{{1,2,3}}
,o
变体将尝试扩展{
,这显然不执行任何操作;在 的情况下,\arrayLength{\myarray}
宏将根据需要扩展一次。请注意 将{1,2,3}
是 的参数\clist_count:n
,因此将删除括号。
还要注意,这是完全可扩展的(可以进入\edef
)。
答案4
我有时在代码中解决这个问题的方法是
\documentclass[tikz]{standalone}
\newcounter{arraycard}
\def\arrayLength#1{%
\setcounter{arraycard}{0}%
\edef\temp{\noexpand\foreach \noexpand\x in #1{%
\noexpand\stepcounter{arraycard}%
}}%
\temp
\the\value{arraycard}%
}
\begin{document}
The length of $\{1,2,3\}$ is \arrayLength{{1,2,3}}.
The length of $\{1,2,\{3,4\},5\}$ is \arrayLength{{1,2,{3,4},5}}.
\def\myarray{{1,2,3,4,5,6}}
The length of \verb|\myarray| is \arrayLength\myarray.
\end{document}
很可能会有同等的东西,但我个人不太\expandafter
喜欢。\expandafter