[Tex/LaTex] How to break out of a loop

loopsprogramming

I have been trying to automate the production of stepped tables that contain conversion factors of all sorts. (See Protrusion of fractions in tabulars).

As part of this I have a rather convoluted macro to convert decimals to fractions. The algorithm works fairly well and sample output is shown below:

enter image description here

As I am trying to catch common fractions as those found in traditional units (1/12, 3/4, 1/60, 1/3 etc), I would like to be able to break out of the loop once a limit is reached. I have tested it using FPifgt or similar but I am getting problems with the double fi. Is there a way out of it?

The code is shown below (apologies for length):

\documentclass{article}
\usepackage{amsmath,fp}
\begin{document}
\makeatletter
\count@=1
\def\DecimalToFraction#1{
%helper macro
\FPset\zero{0}
\FPset\X{#1}

%% Set initial values
\FPadd\X{\X}{0.0000000001} % avoid overflows and divisions by zero
\FPset\Zi{\X}
\FPset\Di{1}
\FPset\Dprevious{0}
%% begin loop
\loop\ifnum\count@<13
%% numerator term
\FPtrunc\temp{\Zi}{0} 
\FPsub\temp{\Zi}{\temp}
%% inverse
\FPdiv\Znext{1}{\temp}
%% Find Dnext
\FPtrunc\IntZnext{\Znext}{0}
%% Di x Int{Zi+1}
\FPmul\temp{\Di}{\IntZnext}
\FPadd\temp{\Di}{\Dprevious}
\FPset\Dnext{\temp}
\FPround\Dnext{\Dnext}{0}

%%% Find Ni+1
\FPmul\temp{\X}{\Dnext}
\FPround\temp{\temp}{0}
\FPset\Nnext{\temp}

\FPdiv\ratio{\Nnext}{\Dnext}

\(Z_i=\Znext\to \Nnext/\Dnext =\ratio\)

\FPset{\Dprevious}{\Dnext}
\FPset{\Di}{\Dprevious}
\FPset{\Zi}{\Znext}

\advance\count@ by1
\repeat
%% end of loop

\gdef\NUM{\Nnext}
\gdef\DEN{\Dnext}

\makeatother
}

\def\Test#1{%
\DecimalToFraction{#1}
The number $#1=\frac{\NUM}{\DEN}$

}
\Test{0.375}
\end{document}

Best Answer

To abort the loop after the current iteration simply \let the internal \iterate macro to \relax. If you want to skip the rest of the loop code you can use a macro defined to \fi\iffalse for this (as Bruno already said).


Abort at end of current iteration:

\documentclass{article}

\begin{document}

\newcount\mycount

\mycount=1

\loop\ifnum\mycount<13

 % Do calculation
 \typeout{Loop: \the\mycount}

 \ifnum\mycount>5
    \let\iterate\relax
 \fi
 \advance\mycount by 1\relax
\repeat

\end{document}

Abort immediately:

\documentclass{article}

\def\breakloop{\fi\iffalse}
\begin{document}

\newcount\mycount

\mycount=1

\loop\ifnum\mycount<13

 % Do calculation
 \typeout{Loop: \the\mycount}

 \ifnum\mycount>5
    \expandafter\breakloop
 \fi

 \typeout{ more }
 \advance\mycount by 1\relax
\repeat

\end{document}

Explanation:

First lets look at the (LaTeX) definitions of \loop and \repeat:

\loop:
\long macro:#1\repeat ->\def \iterate {#1\relax \expandafter \iterate \fi }\iterate \let \iterate \relax . 

\repeat:
\fi. 

As you see \loop stores everything between it and \repeat into \iterate which calls itself. This recursion implements the loop. The \expandafter ensures that no dangling \fis get accumulated. As long the loop \if... is true the text is executed, and \iterate is called again after the \fi. If the conditional is false everything until the \fi is skipped including the \expandafter. However if \iterate is changed to \relax the recursion stops independent of the conditional. Because this happens after the \fi no cleanup is required.

The \breakloop generates a \fi\iffalse. The \fi closes the loop conditional and the \iffalse makes TeX skip everything until the final \fi like the loop conditional would do.


If you need to use FP conditionals inside the loop you have to make them "skip save" first. The problem is that FP define own if switches as macros which aren't recognized when TeX skips over an false path. To fix this define macros like this

\def\xFPiflt#1#2{%
  \FPiflt{#1}{#2}%
    \expandafter\@firstoftwo
  \else
    \expandafter\@secondoftwo
  \fi
}

Then use \xFPiflt\x\y{<true>}{<false>} instead of \FPiflt\x\y <true> \else <false> \fi.