[Tex/LaTex] Position of largest element in a list

expl3programming

Consider the following:

\documentclass{article}

\usepackage{expl3}

\ExplSyntaxOn
  \cs_new_eq:NN \calc \fp_eval:n
\ExplSyntaxOff

\def\valueA{7}
\def\valueB{19}
\def\valueC{41}
\def\valueD{31}
\def\valueE{25}
\def\valueF{17}
\def\valueG{7}
\def\valueH{3}

\begin{document}

\noindent I have the list
\[
\valueA,\valueB,\valueC,\valueD,\valueE,\valueF,\valueG,\valueH
\]
in which the largest element is $\calc{max(\valueA,\valueB,\valueC,\valueD,\valueE,\valueF,\valueG,\valueH)}$ (but that is of no importance to me).\\[\baselineskip]
How can I get \LaTeX{} to extract the position number of the largest element in a list? (In the example above, the answer is of course `$3$'.)

\end{document}

output

Note

It doesn't have to be a LaTeX 3 solution, but I need to compile the original document (i.e., the one I need the solution for) via latex –> dvips –> ps2pdf.

Also, the list is generated via the \def method.

Best Answer

Just using expl3 we can do this using one loop or two. A one loop version needs to work out the maximum value and track the position at the same time:

\documentclass{article}
\usepackage{expl3,xparse}
\ExplSyntaxOn
\NewDocumentCommand \maxposition { m }
  {
    \svend_clist_pos_max:n {#1}
  }
\cs_new:Npn \svend_clist_pos_max:n #1
  {
    \__svend_clist_pos_max:nnnw \c_zero \c_zero { -\c_max_int }
      #1 , \q_recursion_tail , \q_recursion_stop
  }
 \cs_new:Npn \__svend_clist_pos_max:nnnw #1#2#3#4 ,
   {
     \quark_if_recursion_tail_stop_do:nn {#4} {#1}
     \fp_compare:nNnTF {#4} > {#3}
       {
         \__svend_clist_pos_max:fnnw 
           { \int_eval:n { #1 + #2 + \c_one } } \c_zero {#4}
       }
       {
         \__svend_clist_pos_max:nfnw 
           {#1} { \int_eval:n { #2 + \c_one } } {#3}
       }
  }
\cs_generate_variant:Nn \__svend_clist_pos_max:nnnw { f , nf }
\ExplSyntaxOff

\def\valueA{7}
\def\valueB{19}
\def\valueC{41}
\def\valueD{31}
\def\valueE{25}
\def\valueF{17}
\def\valueG{7}
\def\valueH{3}

\begin{document}

\maxposition{\valueA,\valueB,\valueC,\valueD,\valueE,\valueF,\valueG,\valueH}

\end{document}

whereas a two loop version first finds the maximum then the position so there are fewer things to track in one go

\documentclass{article}
\usepackage{expl3,xparse}
\ExplSyntaxOn
\NewDocumentCommand \maxposition { m }
  {
    \svend_clist_pos_max:n {#1}
  }
\cs_new:Npn \svend_clist_pos_max:n #1
  {
    \__svend_clist_pos_max:fn
      {
        \__svend_clist_pos_max:nw { -\c_max_int }
          #1 , \q_recursion_tail , \q_recursion_stop
      } {#1}
  }
\cs_new:Npn \__svend_clist_pos_max:nw #1#2 ,
  {
    \quark_if_recursion_tail_stop_do:nn {#2} {#1}
    \fp_compare:nNnTF {#2} > {#1}
      { \__svend_clist_pos_max:nw {#2} }
      { \__svend_clist_pos_max:nw {#1} }
  }
\cs_new:Npn \__svend_clist_pos_max:nn #1#2
  {
    \__svend_clist_pos_max:nnw { 1 } {#1}
      #2 , \q_recursion_tail , \q_recursion_stop
  }
\cs_generate_variant:Nn \__svend_clist_pos_max:nn { f }
\cs_new:Npn \__svend_clist_pos_max:nnw #1#2#3 ,
  {
    \quark_if_recursion_tail_stop_do:nn {#3} {#1}
    \int_compare:nNnT {#2} = {#3}
      { \use_i_delimit_by_q_recursion_stop:nw {#1} }
    \__svend_clist_pos_max:fnw { \int_eval:n { #1 + \c_one } } {#2}
  }
\cs_generate_variant:Nn \__svend_clist_pos_max:nnw { f }


\cs_new:Npn \__svend_list_max:nw #1#2 ,
  {
    \quark_if_recursion_tail_stop_do:nn {#2} {#1}
    \int_compare:nNnTF {#2} > {#1}
      { \__svend_list_max:nw {#2} }
      { \__svend_list_max:nw {#1} }
  }
\ExplSyntaxOff

\def\valueA{7}
\def\valueB{19}
\def\valueC{41}
\def\valueD{31}
\def\valueE{25}
\def\valueF{17}
\def\valueG{7}
\def\valueH{3}

\begin{document}

\maxposition{\valueA,\valueB,\valueC,\valueD,\valueE,\valueF,\valueG,\valueH}

\end{document}

The idea is first to find the largest entry using one mapping, then to find the position of this entry using a second mapping. Everything is expandable at the code level, so you could use \DeclareExpandableDocumentCommand here if you wanted.

Related Question