如何让 LaTeX 记住一些数字并通过调用另一个命令输出总和?考虑这个例子:
\documentclass{article}
\usepackage{lipsum}
\begin{document}
The price of the first item is \pr{10.23}, that of the second item is
\pr{2.30}.
%% The command \pr{10.23} should print the number 10.23 (perhaps with unit) and then remember it for the total sum.
The total so far is \prsum the overall total sum is \prallsum.
%% should print 12.53 and 14.86.
There is another item about \pr{2.33}.
Not the total sum so far is \prsum and the overall total sum is \prallsum
%% This should print 14.86 in both cases.
\end{document}
答案1
这是一个使用的解决方案minifp
包和一些aux
文件魔法(因此您需要运行文件latex
两次才能获得正确的值)。
\documentclass{article}
\usepackage{minifp}
\makeatletter
\newcommand*\@subtotal{0}
\newcommand*\pr[1]{%
#1%
\MFPadd{\@subtotal}{#1}\@subtotal\relax%
\MFPstrip{\@subtotal}\@subtotal}
\newcommand*\prsum{\@subtotal}
\def\student@allsum{???}
\gdef\prallsum{\student@allsum}
\AtEndDocument{%
\write\@auxout{\string\gdef\string\student@allsum{\@subtotal}}}
\makeatother
\begin{document}
The price of the first item is \pr{10.23}, that of the second item is
\pr{2.30}.
The total so far is \prsum{} the overall total sum is \prallsum.
There is another item about \pr{2.33}.
Not the total sum so far is \prsum{} and the overall total sum is \prallsum.
\end{document}
讨论
命令
\newcommand*\pr[1]{%
#1%
\MFPadd{\@subtotal}{#1}\@subtotal\relax%
\MFPstrip{\@subtotal}\@subtotal}
定义\pr
宏,以便它首先打印输入值(假定为十进制数),然后使用包\MFPadd
的函数将其添加到小计中minifp
。然后使用该函数删除尾随的零\MFPstrip
。(旁注:minifp
执行最多 8 位小数的定点运算。)
该命令\prsum
仅打印小计;注意到在文本过程中,我已编辑了您的文件以改用\prsum{}
,以避免宏随后吞噬空间。如果您仅\prsum
在文本中使用,则可以使用xspace
以避免这种需要。
要打印总和,我们要做的是,在文档末尾(当您写入时,将当前小计\end{document}
写入文件。我们通过将其写为命令定义来实现这一点:在文件上运行后,该文件应包含以下行.aux
pdflatex
.aux
\gdef\student@allsum{14.86}
这一行在 左右被读回到流中\begin{document}
(当aux
读取文件时),因此将 的原始定义覆盖\student@allsum
为???
。我们必须包含原始定义,因为在 的第一次传递之前pdflatex
,命令\student@allsum
将未定义(因为aux
文件是在运行期间生成的,并且在第一次运行之前无法读取)。
请注意,这意味着,就像解析标签引用一样,要更新正确的值,您至少\prallsum
需要运行pdflatex
两次更改文件后。
答案2
您可以使用xparse
和expl3
:
\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\fp_new:N \g_student_sum_fp
\fp_new:N \g_student_total_fp
\NewDocumentCommand{\pr}{m}
{
\fp_eval:n { #1 } % print the value
\fp_gadd:Nn \g_student_sum_fp { #1 } % add to the global container
}
\DeclareExpandableDocumentCommand{\prsum}{}
{
\fp_eval:n { \g_student_sum_fp }
}
\DeclareExpandableDocumentCommand{\prallsum}{}
{
\fp_eval:n { \g_student_total_fp }
}
\AtEndDocument
{
\iow_now:cx { @auxout }
{
\ExplSyntaxOn ^^J
\fp_gset:Nn \exp_not:N \g_student_total_fp { \fp_eval:n { \g_student_sum_fp } } ^^J
\ExplSyntaxOff ^^J
}
}
\ExplSyntaxOff
\begin{document}
The price of the first item is \pr{10.23}, that of the second item is
\pr{2.30}.
The total so far is \prsum{} and the overall total sum is \prallsum.
There is another item about \pr{2.33}.
Now the total sum so far is \prsum{} and the overall total sum is \prallsum.
\end{document}
宏应该足够清晰:\pr
打印给定的值并更新总数;在文档结束时,总数以适当的方式写在辅助文件中。
\g_student_sum_fp
如果和的值不同,则可能会添加警告\g_student_total_fp
,告诉用户需要重新运行 LaTeX:将代码从更改\AtEndDocument
为\ExplSyntaxOff
\AtEndDocument
{
\iow_now:cx { @auxout }
{
\ExplSyntaxOn ^^J
\fp_gset:Nn \exp_not:N \g_student_total_fp { \fp_eval:n { \g_student_sum_fp } } ^^J
\ExplSyntaxOff ^^J
}
\fp_compare:nF { \g_student_sum_fp = \g_student_total_fp }
{
\msg_warning:nn { student/sum } { rerun }
}
}
\msg_new:nnn { student/sum } { rerun }
{
Value~for~\exp_not:N \prallsum has~changed.~Rerun~LaTeX
}
\ExplSyntaxOff
如果值与上次运行相比发生了变化,则会收到警告
*************************************************
* student/sum warning: "rerun"
*
* Value for \prallsum has changed. Rerun LaTeX
*************************************************