# Trying to eliminate stack overflow during recursion

I was looking at this question, Order table by row alphabeticaly, and thought, "that OP needs some sort of bubble sorter."

I had done a numerical bubble sorter here, Using LaTeX to compact a list of numbers (it actually sorted reference numbers and also compacted sequential references) and saw that I had also done an alpha-bubble sorter, as well here, Sorting a comma-separated list with LaTeX?.

Unfortunately, I discovered that the alpha-bubble sorter was prone to stack overflow from the recursion, when the list became larger. So I endeavored to streamline things here, rewriting the code. It is able to handle larger list sizes than the cited version, but as seen the included MWE, if I uncomment that one additional item in the list (encontrar), I blow the stack.

Now I know that, in theory, the way to avoid blowing the stack during a conditional recursion is to ...\expandafter\recursion\fi. However, I've never been good at mastering that technique when the recursion is nested deep in an \if block, nor when the recursion is not a mere macro name, but instead a ...\recursion<data-block>\relax\fi blob.

David C tells me that the stack size is set in texmf.cnf, and while I haven't yet found that file in my MikTeX installation, I realize that enlarging the stack is a poor fix in lieu of better code.

I also have not yet put in the \the\lccode tweaks to overlook lettercase in the search, but one thing at a time.

I figure a general LaTeX alpha-bubble sorter would be a useful thing to more than just me, so I post it here as a question, in hopes that it can be beat into submission and become a useful tool.

In the MWE below, the routine \alphabubblesorter calls on \sortlist to do the primary bubble sort, with \picknext being called upon for cases that share the same first letter. So \sortlist is recursive through the data list, and \picknext is recursive through the letter list of two comparitive entries.

\documentclass[10pt]{article}
\newcommand\alphabubblesort[1]{\def\sortedlist{}\expandafter\sortlist#1,9,\relax}
\def\sortlist#1#2,#3#4,#5\relax{%
\ifnum#3=9\relax%
\edef\sortedlist{\sortedlist#1#2}%
\else
\ifnum#1<#3\relax%
\edef\sortedlist{\sortedlist#1#2,}%
\sortlist#3#4,#5\relax%
\else%
\ifnum#1>#3\relax%
\let\tmp\sortedlist%
\def\sortedlist{}%
\expandafter\sortlist\tmp#3#4,#1#2,#5\relax%
\else%
\picknext#2!,#4!\relax%
\if F\flipflop%
\edef\sortedlist{\sortedlist#1#2,}%
\sortlist#3#4,#5\relax%
\else%
\let\tmp\sortedlist%
\def\sortedlist{}%
\expandafter\sortlist\tmp#3#4,#1#2,#5\relax%
\fi%
\fi%
\fi%
\fi%
}
\def\picknext#1#2,#3#4\relax{%
\ifnum#1<#3\relax
\xdef\flipflop{F}%
\else%
\ifnum#1>#3\relax%
\xdef\flipflop{T}%
\else%
\picknext#2!,#4!\relax%
\fi%
\fi%
}
\begin{document}
\alphabubblesort{da,cc,ca,eda,edc,edb,ef,ec,ed,eb,edzq,ba,e,fa,waaa,wa,qa}\sortedlist

\def\mydata{%
Spanish     ,
ser         ,
haber       ,
estar       ,
tener       ,
hacer       ,
poder       ,
decir       ,
ir          ,
ver         ,
dar         ,
saber       ,
querer      ,
llegar      ,
pasar       ,
deber       ,
poner       ,
parecer     ,
quedar      ,
creer       ,
hablar      ,
llevar      ,
dejar       ,
seguir      ,
%encontrar   ,
llamar       }
\alphabubblesort{\mydata}\sortedlist
\end{document}


• what test input should i use to test (not) blowing the stack) – David Carlisle May 31 '17 at 19:29
• There is (upon edit) a commented line in the last data entry (encontrar). Commented, it works; uncommented, it breaks. – Steven B. Segletes May 31 '17 at 19:30
• ah spotted....... – David Carlisle May 31 '17 at 19:32
• I see problems if there's ñ in the list. Accents should not be a problem with verbs. – egreg May 31 '17 at 20:39
• @egreg Yes, I know my other alpha-sorter did some additional expansions to handle diacritics. I will have to consider that next... – Steven B. Segletes May 31 '17 at 20:43

something like this allows the parameter stack to be popped before the recursive call

\documentclass[10pt]{article}
\newcommand\alphabubblesort[1]{\def\sortedlist{}\expandafter\sortlist#1,zzzzzzzzzz,\relax}
\def\sortlist#1#2,#3#4,#5\relax{%
\let\next\relax
\ifnum#3=z\relax%
\edef\sortedlist{\sortedlist#1#2}%
\else
\ifnum#1<#3\relax%
\edef\sortedlist{\sortedlist#1#2,}%
\def\next{\sortlist#3#4,#5\relax}%
\else%
\ifnum#1>#3\relax%
\let\tmp\sortedlist%
\def\sortedlist{}%
\def\next{\expandafter\sortlist\tmp#3#4,#1#2,#5\relax}%
\else%
\picknext#2!,#4!\relax%
\if F\flipflop%
\edef\sortedlist{\sortedlist#1#2,}%
\def\next{\sortlist#3#4,#5\relax}%
\else%
\let\tmp\sortedlist%
\def\sortedlist{}%
\def\next{\expandafter\sortlist\tmp#3#4,#1#2,#5\relax}%
\fi%
\fi%
\fi%
\fi%
\next
}
\def\picknext#1#2,#3#4\relax{%
\ifnum#1<#3\relax
\xdef\flipflop{F}%
\else%
\ifnum#1>#3\relax%
\xdef\flipflop{T}%
\else%
\ZZfifi{\picknext#2!,#4!\relax}%
\fi%
\fi%
}

\def\ZZfifi#1\fi\fi{\fi\fi#1}

\begin{document}
\alphabubblesort{30,2c,2a,4da,4dc,4db,4f,4c,4d,2b,4dzq,10,4,50,W0,w0,q0}\sortedlist

\def\mydata{%
Spanish     ,
ser         ,
haber       ,
estar       ,
tener       ,
hacer       ,
poder       ,
decir       ,
ir          ,
ver         ,
dar         ,
saber       ,
querer      ,
llegar      ,
pasar       ,
deber       ,
poner       ,
parecer     ,
quedar      ,
creer       ,
hablar      ,
llevar      ,
dejar       ,
seguir      ,
encontrar   ,
llamar       }
\alphabubblesort{\mydata}\sortedlist
\end{document}


I hate to meddle in David's amazing answer (Steven here), but I realized I could streamline things further by going directly to \picknext. I also added the \the\lccode bit to screen out lettercase.

So, in this incarnation, \sortlist merely performs the swap of two adjacent list items, if appropriate, whereas \picknext makes the decision on whether the swap should be made. (\sortlist no longer compares anything except for the end of list condition).

Since David was too modest to lay out the specifics of his changes, I will just call reader attention to his use of \next, which is a not so unusual way of moving stuff outside of a nested \if structure. But the crowning glory comes with his creation and use of \ZZfifi, which (if I understand it) absorbs the remaining two \fis of the current recursion, and then pops 2 of them off the stack as the first step in the next recursion. Masterful!

\documentclass[10pt]{article}
\newcommand\alphabubblesort[1]{\def\sortedlist{}\expandafter\sortlist#1,\cr,\relax}
\def\sortlist#1,#2,#3\relax{%
\let\next\relax
\ifx\cr#2\relax%
\edef\sortedlist{\sortedlist#1}%
\else
\picknext#1!,#2!\relax%
\if F\flipflop%
\edef\sortedlist{\sortedlist#1,}%
\def\next{\sortlist#2,#3\relax}%
\else%
\let\tmp\sortedlist%
\def\sortedlist{}%
\def\next{\expandafter\sortlist\tmp#2,#1,#3\relax}%
\fi%
\fi%
\next
}
\def\picknext#1#2,#3#4\relax{%
\ifnum\the\lccode#1<\the\lccode#3\relax
\xdef\flipflop{F}%
\else%
\ifnum\the\lccode#1>\the\lccode#3\relax%
\xdef\flipflop{T}%
\else%
\ZZfifi{\picknext#2!,#4!\relax}%
\fi%
\fi%
}
\def\ZZfifi#1\fi\fi{\fi\fi#1}
\begin{document}
\alphabubblesort{da,cc,ca,eda,edc,edb,ef,ec,ed,eb,edzq,ba,e,fa,waaa,wa,qa}\sortedlist

\def\mydata{
Spanish, ser, haber, estar, tener, hacer, poder, decir, ir, ver, dar, saber, querer,
llegar, pasar, deber, poner, parecer, quedar, creer, hablar, llevar, dejar,
seguir, encontrar, llamar}
\alphabubblesort{\mydata}\sortedlist
\end{document}


• This will take a while for me to chew on, because you have some really neat tricks here. I get the one with \next, but I am trying to wrap my mind around\ZZfifi. – Steven B. Segletes May 31 '17 at 19:58
• @StevenB.Segletes I like to give commands self explanatory names. – David Carlisle May 31 '17 at 20:02
• Well I know that if it has a Z it is special, and so I know that 2 Zs are the cat's meow. BTW, I edited your answer to add the \the\lccode fix for lettercase. – Steven B. Segletes May 31 '17 at 20:04
• \ZZfifi is a magnificent achievement in stack manipulation. I applaud your ingenuity. – Steven B. Segletes May 31 '17 at 20:26
• @StevenB.Segletes sure it wasn't my idea: more "common folklore";-) – David Carlisle May 31 '17 at 20:52