我一直在尝试自动生成包含各种转换因子的阶梯表。(见表格中分数的突出)。
作为其中的一部分,我有一个相当复杂的宏来将小数转换为分数。该算法运行良好,示例输出如下所示:
当我尝试捕捉传统单位中的常见分数(1/12、3/4、1/60、1/3 等)时,我希望能够在达到限制后跳出循环。我已经使用FPifgt
或类似程序对其进行了测试,但在 double 方面遇到了问题fi
。有办法解决吗?
代码如下所示(抱歉太长):
\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}
答案1
要在当前迭代后中止循环,只需将\let
内部\iterate
宏设置为\relax
即可。如果您想跳过循环的其余部分代码,可以使用\fi\iffalse
为此定义的宏(正如 Bruno 所说)。
在当前迭代结束时中止:
\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}
立即中止:
\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}
解释:
\loop
首先让我们看一下和的(LaTeX)定义\repeat
:
\loop:
\long macro:#1\repeat ->\def \iterate {#1\relax \expandafter \iterate \fi }\iterate \let \iterate \relax .
\repeat:
\fi.
如您所见,\loop
它将所有内容存储在它和调用自身的中间\repeat
。\iterate
此递归实现了循环。确保不会累积\expandafter
任何悬空\fi
。只要循环\if...
为真,就会执行文本,然后\iterate
再次调用后。\fi
如果条件为假,则\fi
跳过直到的所有内容,包括\expandafter
。但是,如果\iterate
更改为,则\relax
递归将独立于条件停止。因为这发生在之后,因此\fi
不需要进行清理。
生成\breakloop
一个\fi\iffalse
。\fi
关闭循环条件,并使\iffalse
TeX 跳过所有内容直到最后,\fi
就像循环条件一样。
如果你需要在循环中使用 FP 条件,你必须先让它们“跳过保存”。问题是 FP 将自己的if
开关定义为宏当 TeX 跳过错误的路径。要修复此问题,请定义如下宏
\def\xFPiflt#1#2{%
\FPiflt{#1}{#2}%
\expandafter\@firstoftwo
\else
\expandafter\@secondoftwo
\fi
}
然后使用\xFPiflt\x\y{<true>}{<false>}
而不是\FPiflt\x\y <true> \else <false> \fi
。
答案2
循环具有以下结构
\loop
...
\if...
...
\repeat
如果您在\if
和之间\repeat
,您可以发出 来\fi\iffalse
关闭当前\if
并跳过所有文本直到\repeat
(或者更确切地说,直到\fi
产生的\loop...\repeat
)。如果您的循环是 ,这将不起作用\loop...\if...\else...\repeat
。
更强大的解决方案是将其放置\@gobble\mysentinel
在之前\repeat
,然后使用以下宏退出循环:
\def\Iwanttobreakfree#1\mysentinel{\iffalse}
这将丢弃所有标记,直到\mysentinel
,包括\@gobble
,并且\iffalse
确保循环停止。