2

I read a clist and create a prop-list which should behave as a hash in which the value for a given key is the number of occurences of this value in the clist. Below my code which, so far, does work as expected.

\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}

As the log shows, it doesn't work as expected when a key occurs more than once:

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}

Thanks for reading, any help will be greatly appreciated

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}
2
  • 1
    Bonjour chez TeX.SX! – AlexG Feb 24 '17 at 12:17
  • In your original code, replace one \prop_gput:NVn by \prop_gput:NVV (to store the valur of its third argument \l_tmpa_int). – Bruno Le Floch Feb 24 '17 at 16:53
4

Easier (and shorter in my point of view)

Use seq variable, populate it with the #1 argument and add the keys value in a \seq_map_inline variable, branching with \prop_if_in.

There is no need of pop and push etc or fiddling with tl variables, in my point of view.

If the values should exist outside, use a global prop variable.

\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}

enter image description here

4
  • As can be seen from the screen shot, the counting is correct. The % are just for organizing, not for spurious spaces – user31729 Feb 24 '17 at 12:27
  • \prop_item:nn is inevitably not very fast. It is faster to use the non-expandable functions \prop_get etc. Similarly \prop_if_in is slow. – Bruno Le Floch Feb 24 '17 at 16:54
  • @BrunoLeFloch: I know, but it is expandable, contrary to \prop_get. And I like \prop_item very much ;-) (And egreg does use both \prop_if_in and \prop_item as well...) – user31729 Feb 24 '17 at 16:55
  • Here you don't need expandability. Actually, speedwise the best would be to use \prop_get:NnNTF for the branching so that in the "true" branch you directly have the item itself. I didn't look yet at egreg's answer. – Bruno Le Floch Feb 24 '17 at 17:02
4

Since you know the value of each key is an integer, you can use it in \int_eval:n through \prop_item:Nn, which is expandable.

I propose an interface where you can accumulate in different property lists and also choose whether clearing the property list or not (with the *-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

Here's the result on the console.

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]

? 

As Bruno LeFloch remarks in comments, \prop_item:Nn is slower than \prop_get:NnN; here's a version using the latter.

\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
2
  • See my comments on the other answer, \prop_item:nn is nice because of expandability but it is slower than non-expandable functions. – Bruno Le Floch Feb 24 '17 at 17:03
  • @BrunoLeFloch Yes, I know it's slow. I'll add a faster version with \prop_get:NnN – egreg Feb 24 '17 at 17:12

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.