11

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.

1
  • 2
    I updated the tag from latex3 to expl3 because expl3 concerns the programming framework while latex3 is (supposed to be) more concerned with the project or its architecture :) I hope I'm not stepping on any toes -- feel free to add latex3 back if you think it should be so. Mar 5, 2015 at 20:25

4 Answers 4

10

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.

5
  • What would be the one-loop algorithm? (Along the lines of an exercise for the reader...) Mar 5, 2015 at 20:26
  • Note that to do the job expandably I have to handle the loop(s) myself rather than using \clist_map_... (which would require variable setting).
    – Joseph Wright
    Mar 5, 2015 at 20:33
  • @SeanAllred I ended up adding it :-) Basic plan is to work out the maximum whilst tracking its position and how many items have been seen since the maximum. Just a question of passing everything as args!
    – Joseph Wright
    Mar 5, 2015 at 20:34
  • @JosephWright Which of the two approaches compiles the fastest? Mar 5, 2015 at 23:48
  • 1
    @SvendTveskæg Untested, but I'd guess the one-loop version
    – Joseph Wright
    Mar 6, 2015 at 6:59
13

Here is an etoolbox:

enter image description here

\documentclass{article}

\usepackage{expl3,etoolbox}

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

\makeatletter
\newcounter{maxindex}
\newcommand{\findmaxindex}[1]{%
  \@tempcnta=0% Counter for stepping through elements
  \setlength{\@tempdima}{-\maxdimen}% "Smallest" number as a length/dimension
  \renewcommand*{\do}[1]{%
    \advance\@tempcnta by 1% Step to next element
    \ifdimgreater{##1pt}{\@tempdima}{\setlength{\@tempdima}{##1pt}\setcounter{maxindex}{\@tempcnta}}{}}% Found larger element
  \docsvlist{#1}% process list to find maximum number and index
}
\makeatother

\setlength{\parindent}{0pt}% Just for this example
\begin{document}

I have the list
\[
  7, 19, 41, 31, 25, 17, 7, 3
\]
in which the largest element is $\calc{max(7, 19, 41, 31, 25, 17, 7, 3)}$.

\findmaxindex{7, 19, 41, 31, 25, 17, 7, 3}% Find maximum number index
The position number of the largest element in this \themaxindex.

\bigskip

I have the list
\[
  \valueH,\valueG,\valueF,\valueE,\valueD,\valueC,\valueB,\valueA
\]
in which the largest element is $\calc{max(\valueH,\valueG,\valueF,\valueE,\valueD,\valueC,\valueB,\valueA)}$.

\findmaxindex{\valueH,\valueG,\valueF,\valueE,\valueD,\valueC,\valueB,\valueA}% Find maximum number index
The position number of the largest element in this \themaxindex.

\end{document}

Note that it will choose as index of the first elements that is largest. The index of the maximum number is stored in the counter maxindex, available for print via \themaxindent.

The idea is to step through the list and mark each element that is found to be greater than the previous greatest element found. Using dimensions allows for working with decimal numbers.

0
11

Another expandable solution. No package used. (for this situation dealing only with integers, for decimals one can replace \ifnum with \ifdim up to some extent; for even more complicated situations there is, among others, xint).

\documentclass{article}

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

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

\makeatletter
% the two routines \maxoflist and \maxposition are not optimal if the
% list elements require some costly expansion to compute their values
% this could be improved, if needed, to do this expansion only once
% I here design the thing for either litteral digit tokens or 
% \count registers or the like

% naturally one could also do a single routine with a two element
% output: (earliest) position and value of the maximum

% maximum
\def\maxoflist #1{\romannumeral0\expandafter
                    \maxoflist@a\romannumeral-`0#1,,}
\def\maxoflist@a #1,{\maxoflist@c {\numexpr#1\relax}}
\def\maxoflist@c #1#2,{%
    \ifx\relax #2\relax\expandafter\maxoflist@end
    \else\ifnum#2>#1\expandafter\expandafter\expandafter\maxoflist@update
               \else\expandafter\expandafter\expandafter\maxoflist@next
    \fi\fi {#1}{#2}}
\def\maxoflist@end    #1#2{\expandafter\space\the#1}
\def\maxoflist@update #1#2{\maxoflist@c {\numexpr#2\relax}}
\def\maxoflist@next   #1#2{\maxoflist@c {#1}}

% max position
\def\maxposition #1{\romannumeral0\expandafter
                    \maxposition@a\romannumeral-`0#1,,}
\def\maxposition@a #1,{\maxposition@c 21{\numexpr#1\relax}}
\def\maxposition@b #1%
   {\expandafter\maxposition@c\expandafter {\the\numexpr #1+\@ne}}%
\def\maxposition@c #1#2#3#4,{%
    \ifx\relax #4\relax\expandafter\maxposition@end
    \else\ifnum#4>#3\expandafter\expandafter\expandafter\maxposition@update
               \else\expandafter\expandafter\expandafter\maxposition@next
    \fi\fi {#1}{#2}{#3}{#4}}
\def\maxposition@end #1#2#3#4{ #2}
\def\maxposition@update #1#2#3#4{\maxposition@b {#1}{#1}{\numexpr#4\relax}}
\def\maxposition@next   #1#2#3#4{\maxposition@b {#1}{#2}{#3}}
\makeatother


\begin{document}

\noindent I have the list
\[
\mylist
\]
in which the largest element is $\maxoflist\mylist$ (but that is 
of no importance to me).\\[\baselineskip]
How can I get \LaTeX{} to extracting the position number 
of the largest element in a list? (In the example above, 
the answer is of course `$\maxposition\mylist$'.)

\end{document}

maxposition

3
  • 1
    my comment about a routine doing both is a bit awkward: \maxposition already does both, it is only that in the end it prints only the position #2 and not the max value \the#3.
    – user4686
    Mar 5, 2015 at 21:33
  • \valueE is repeated, by mistake in copy paste (I wonder how...)
    – user4686
    Mar 5, 2015 at 21:36
  • 1
    thanks... but the mistake wasn't when I copied pasted to post the answer, but when I manipulated my emacs buffer. This shows in the image ! we are doomed to maintain these comments for the edification of future readers ;-)
    – user4686
    Mar 5, 2015 at 21:46
1

With pgfmath:

enter image description here

\documentclass[a4paper]{article}
\usepackage{tikz}
\begin{document}
\def\mylist{7, 19, 41, 31, 25, 17, 7, 3}
\pgfmathtruncatemacro\Max{max(\mylist)}

\foreach[count=\Pos] \k in \mylist {
\pgfmathsetmacro\test{\k==\Max ?  \Pos :  0}
\ifnum\test=0 \else \xdef\MaxPos{\test} \fi
}

In \{\mylist\} the maximum is  {\Max} at list-position \MaxPos.
\end{document}

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .