我读取一个 clist 并创建一个 prop-list,它应该表现为一个哈希,其中给定键的值是 clist 中此值的出现次数。下面是我的代码,到目前为止,它确实按预期工作。
\documentclass{article}
\usepackage[T1]{fontenc}
\usepackage[utf8]{inputenc}
\usepackage[a4paper]{geometry}
\usepackage{lmodern}
\usepackage[log-declarations=false]{xparse}
\ExplSyntaxOn
\NewDocumentCommand{\Accumulate}
{ m }
{
\clist_clear_new:N \l_tmpa_clist
\clist_set:Nn \l_tmpa_clist {#1}
\prop_clear_new:N \l_tmpa_prop
\tl_clear:N \l_tmpa_tl
\tl_clear:N \l_tmpb_tl
\bool_set_true:N \l_tmpa_bool % the clist is not void
\bool_do_while:Nn \l_tmpa_bool
{
\par
\clist_pop:NNTF \l_tmpa_clist \l_tmpa_tl
{
"l_tmpa_tl"~ \tl_use:N \l_tmpa_tl \quad{}
\prop_if_in:NVTF \l_tmpa_prop \l_tmpa_tl
{
% l_tmpa is a key in hash l_tmpa_prop
% we get the associated value
\prop_get:NVN \l_tmpa_prop \l_tmpa_tl \l_tmpb_tl
\tl_show:N \l_tmpb_tl
"l_tmpb_tl"~ \tl_use:N \l_tmpb_tl
% we want it as a number
\int_set:Nn \l_tmpb_int { \l_tmpb_tl }
\int_show:N \l_tmpb_int
\par "l_tmpb_int"~ \int_to_arabic:n { \l_tmpb_tl }
\int_gincr:N \l_tmpb_int
% after that the preceding value should be incremented
\prop_gput:NVn \l_tmpa_prop \l_tmpa_tl { \l_tmpb_int }
}
{
% l_tmpa is not yet a key in hash l_tmpa_prop
\prop_gput:NVn \l_tmpa_prop \l_tmpa_tl { 1 }
}
\prop_show:N \l_tmpa_prop
}
{
\bool_set_false:N \l_tmpa_bool
}
}
}
\ExplSyntaxOff
\begin{document}
\Accumulate{1, 2, 3}
\par
\Accumulate{1, 2, 2}
\end{document}
如日志所示,当某个键出现多次时,它不会按预期工作:
The property list \l_tmpa_prop contains the pairs (without outer braces):
> {1} => {1}.
<recently read> }
l.63 \Accumulate{1, 2, 3}
The property list \l_tmpa_prop contains the pairs (without outer braces):
> {1} => {1}
> {2} => {1}.
<recently read> }
l.63 \Accumulate{1, 2, 3}
The property list \l_tmpa_prop contains the pairs (without outer braces):
> {1} => {1}
> {2} => {1}
> {3} => {1}.
<recently read> }
l.63 \Accumulate{1, 2, 3}
The property list \l_tmpa_prop contains the pairs (without outer braces):
> {1} => {1}.
<recently read> }
l.67 \Accumulate{1, 2, 2}
The property list \l_tmpa_prop contains the pairs (without outer braces):
> {1} => {1}
> {2} => {1}.
<recently read> }
l.67 \Accumulate{1, 2, 2}
> \l_tmpb_tl=1.
<recently read> }
l.67 \Accumulate{1, 2, 2}
> \l_tmpb_int=1.
<recently read> }
l.67 \Accumulate{1, 2, 2}
感谢您的阅读,任何帮助都将不胜感激
The property list \l_tmpa_prop contains the pairs (without outer braces):
> {1} => {1}
> {2} => {\l_tmpb_int }.
<recently read> }
l.67 \Accumulate{1, 2, 2}
答案1
更简单(在我看来也更短)
使用seq
变量,用参数填充它#1
并在变量中添加键值\seq_map_inline
,用 进行分支\prop_if_in
。
在我看来,没有必要pop
等等push
或摆弄变量。tl
如果值应该存在于外部,请使用全局prop
变量。
\documentclass{article}
\usepackage[T1]{fontenc}
\usepackage[utf8]{inputenc}
\usepackage[a4paper]{geometry}
\usepackage{lmodern}
\usepackage[log-declarations=false]{xparse}
\ExplSyntaxOn
\cs_generate_variant:Nn \int_set:Nn {Nx}
\NewDocumentCommand{\Accu}{m}{%
\prop_clear:N \l_tmpa_prop
\seq_set_from_clist:Nn \l_tmpa_seq {#1}%
\seq_map_inline:Nn \l_tmpa_seq {%
\prop_if_in:NnTF \l_tmpa_prop {##1} {%
\int_set:Nx \l_tmpa_int {\prop_item:Nn {\l_tmpa_prop} {##1}}
\int_incr:N \l_tmpa_int
\prop_put:Nnx \l_tmpa_prop {##1} {\int_use:N \l_tmpa_int }
}{%
\prop_put:Nnn \l_tmpa_prop {##1} {1}
}
}
\prop_map_inline:Nn \l_tmpa_prop {%
##1~ ##2\par
}
}
\ExplSyntaxOff
\begin{document}
\Accu{1,2,3}
\Accu{1,2,2,3,3,3,4,1,4}
\end{document}
答案2
由于您知道每个键的值都是一个整数,因此您可以在\int_eval:n
其中使用它\prop_item:Nn
,它是可扩展的。
我提出了一个界面,您可以在其中累积不同的属性列表,还可以选择是否清除属性列表(使用-variant *
)。
\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\NewDocumentCommand{\Accumulate}{sO{standard}m}
{
% some administrative tasks
\IfBooleanTF{#1}
{% no *: clear the property list, creating it if necessary
\prop_clear_new:c { l_tds_accumulate_#2_prop }
}
{% * variant: don't clear the property list, creating it if necessary
\prop_if_exist:cF { l_tds_accumulate_#2_prop }
{
\prop_new:c { l_tds_accumulate_#2_prop }
}
}
% the main function
\tds_accumulate:nn { #2 } { #3 }
}
\cs_new_protected:Nn \tds_accumulate:nn
{
% do a mapping on the input
\clist_map_inline:nn { #2 }
{
\tds_accumulate_check:nn { #1 } { ##1 }
}
}
\cs_new_protected:Nn \tds_accumulate_check:nn
{
\prop_if_in:cnTF { l_tds_accumulate_#1_prop } { #2 }
{% the key is already present: increment its value
\prop_put:cnx { l_tds_accumulate_#1_prop } { #2 }
{ \int_eval:n { \prop_item:cn { l_tds_accumulate_#1_prop }{#2} + 1 } }
}
{% the key is new: put in the value 1
\prop_put:cnn { l_tds_accumulate_#1_prop } { #2 } { 1 }
}
}
\NewDocumentCommand{\showaccumulate}{O{standard}}
{
\prop_show:c { l_tds_accumulate_#1_prop }
}
\ExplSyntaxOff
\Accumulate{1, 2, 3}
\showaccumulate
\Accumulate{1, 2, 2}
\showaccumulate
\Accumulate*{1,2,1,2,1,2,3}
\showaccumulate
\Accumulate[new]{ab,ac,ab,abc}
\showaccumulate[new]
\stop
这是控制台上的结果。
The property list \l_tds_accumulate_standard_prop contains the pairs (without
outer braces):
> {1} => {1}
> {2} => {1}
> {3} => {1}.
<recently read> }
l.50
?
The property list \l_tds_accumulate_standard_prop contains the pairs (without
outer braces):
> {1} => {1}
> {2} => {2}.
<recently read> }
l.53
?
The property list \l_tds_accumulate_standard_prop contains the pairs (without
outer braces):
> {1} => {4}
> {2} => {5}
> {3} => {1}.
<recently read> }
l.56
?
The property list \l_tds_accumulate_new_prop contains the pairs (without outer
braces):
> {ab} => {2}
> {ac} => {1}
> {abc} => {1}.
<recently read> }
l.58 \showaccumulate[new]
?
正如 Bruno LeFloch 在评论中所说,\prop_item:Nn
比 慢\prop_get:NnN
;这是一个使用后者的版本。
\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\NewDocumentCommand{\Accumulate}{sO{standard}m}
{
% some administrative tasks
\IfBooleanTF{#1}
{% no *: clear the property list, creating it if necessary
\prop_clear_new:c { l_tds_accumulate_#2_prop }
}
{% * variant: don't clear the property list, creating it if necessary
\prop_if_exist:cF { l_tds_accumulate_#2_prop }
{
\prop_new:c { l_tds_accumulate_#2_prop }
}
}
% the main function
\tds_accumulate:nn { #2 } { #3 }
}
\cs_new_protected:Nn \tds_accumulate:nn
{
% do a mapping on the input
\clist_map_inline:nn { #2 }
{
\tds_accumulate_check:nn { #1 } { ##1 }
}
}
\cs_new_protected:Nn \tds_accumulate_check:nn
{% check whether the key already exists
\prop_get:cnNTF { l_tds_accumulate_#1_prop } { #2 } \l__tds_accumulate_temp_tl
{% the key already exists: increment its value
\prop_put:cnx { l_tds_accumulate_#1_prop } { #2 }
{ \int_eval:n { \l__tds_accumulate_temp_tl + 1 } }
}
{% the key doesn't exist: put 1
\prop_put:cnn { l_tds_accumulate_#1_prop } { #2 } { 1 }
}
}
\cs_generate_variant:Nn \prop_get:NnNTF { c }
\NewDocumentCommand{\showaccumulate}{O{standard}}
{
\prop_show:c { l_tds_accumulate_#1_prop }
}
\ExplSyntaxOff
\Accumulate{1, 2, 3}
\showaccumulate
\Accumulate{1, 2, 2}
\showaccumulate
\Accumulate*{1,2,1,2,1,2,3}
\showaccumulate
\Accumulate[new]{ab,ac,ab,abc}
\showaccumulate[new]
\stop