错误代码:
\documentclass{article}
\usepackage{fp}
\usepackage{numprint}
\newcommand{\ratio}{%
\FPdiv\result{\x}{\y}%
\FPround\result\result{2}%
\result%
}
\begin{document}
\FPset\x{2}
\FPset\y{3}
\numprint{\ratio}
\end{document}
LaTeX 抱怨:
! 未定义控制序列。\next ->\@nil
l.14 \numprint{\ratio}
我如何评估\ratio
并打印它\numprint
?
答案1
宏\numprint
需要输入一串字符,而不是生成它的指令。对于fp
,这些指令涉及赋值,而\numprint
只能处理仅扩展为正确格式的数字的控制序列。因此必须提前生成字符串。(感谢 jfbu 注意到宏可以在 的参数中使用\numprint
,只要它们扩展为具有正确格式的字符串即可。)
我建议您定义一个新命令,将 FP 表达式作为参数,可能是一个宏:
\documentclass{article}
\usepackage{fp}
\usepackage{numprint}
\newcommand{\numprintexpr}[2][\result]{%
#2\relax\numprint{#1}}
\newcommand{\ratio}{%
\FPdiv\result{\x}{\y}%
\FPround\result\result{2}%
}
\begin{document}
\FPset\x{2}
\FPset\y{3}
\numprintexpr{\ratio}
\numprintexpr[\foo]{%
\FPset\x{24}%
\FPset\y{19}%
\FPdiv\foo{\x}{\y}%
\FPround\foo\foo{2}
}
\end{document}
可选参数(默认\result
)告诉\numprintexpr
哪个控制序列存储最终结果。
使用定点设施的不同实现expl3
:
\documentclass{article}
\usepackage{xparse}
\usepackage{numprint}
\ExplSyntaxOn
\DeclareExpandableDocumentCommand{\fpeval}{m}
{
\fp_eval:n { #1 }
}
\NewDocumentCommand{\fpset}{mm}
{
\fp_zero_new:N #1
\fp_set:Nn #1 { #2 }
}
\cs_new_protected:Npn \feklee_numprint:n #1
{
\numprint { #1 }
}
\NewDocumentCommand{\numprintexpr}{m}
{
\feklee_numprint:n { \fp_eval:n { #1 } }
}
\ExplSyntaxOff
\newcommand{\ratio}{round(\x/\y,2)}
\begin{document}
\fpset\x{2}
\fpset\y{3}
\numprintexpr{\ratio}
\numprintexpr{round(pi/4,8)}
\end{document}
语法不同,但比 更简单fp
。
答案2
将 x/y 值传递到宏本身:
\documentclass{article}
\usepackage{fp}
\usepackage{numprint}
\newcommand\ratio[3]{%
\FPdiv#3{#1}{#2}%
\FPround{#3}{4}{4}}
\begin{document}
\ratio{2}{3}\result
\result
\numprint{\result}
\end{document}
答案3
这个问题已经有几个答案了,但似乎没有人建议使用\edef
。了解这一点可能对您在其他情况下有用,所以让我描述一下这个替代解决方案。
该\edef
原语与所定义的宏类似\def
,但会扩展其替换文本。以下是如何重写示例:
\documentclass{article}
\usepackage{fp}
\usepackage{numprint}
\newcommand{\ratio}{%
\FPdiv\result{\x}{\y}%
\FPround\result\result{2}%
}
\begin{document}
\FPset\x{2}
\FPset\y{3}
\begingroup
\ratio
\edef\next{\noexpand\numprint{\result}%
\expandafter
\endgroup
\next
\end{document}
看到计算和用法现在是分开的。 的工作\ratio
仅限于计算比率并将其存储在宏中。
TeX 的工作方式与传统编程语言非常不同,传统编程语言可以像你之前那样编写函数。事实上,它是一种基于扩展和重写的宏语言。因此,如果你写
\numprint{\ratio}
你有绝对无法控制如何解释由 生成的标记。生成一个替换文本为 的宏\ratio
会更容易,就像我在建议中所做的那样。请注意,整个代码片段实际上在处理过程中被替换。\next
\numprint{0.67}
\begingroup…\next
\numprint{0.67}
如果您正在学习 TeX 编程,您应该考虑学习基于\edef
、\expandafter
和\noexpand
寄存器的这些方法。
答案4
您要求的解决方案是保留原始文件\ratio
,然后使用 打印结果\numprint
。操作方法如下:
[更新:已编辑以避免全局分配]
[更新2:\fFPedef
如果想要按照这个答案底部指示的方向操作,则添加的宏是必需的]
[更新3:删除了一些多余的\expandafter
]
[更新 4:修正了上面的拼写错误(\fFPedef
上面是我之前的选择\fFPset
),只是为了将其移到列表的上部,并有机会获得更多的赞成票:)
]
\documentclass{article}
\usepackage{fp}
\usepackage{numprint}
% ORIGINAL \ratio
% by convention its result is in ... \result
\newcommand{\ratio}{%
\FPdiv\result{\x}{\y}%
\FPround\result\result{2}%
\result%
}
% WRAPPER to \numprint
% Must be applied to things like \ratio which compute a \result
\def\FPnumprint #1{%
\setbox0 \hbox{\def\result{#1}#1\expandafter}\expandafter
\numprint\expandafter {\result}}
\begin{document}\thispagestyle{empty}
\FPset\x{2}
\FPset\y{3}
% works
\FPnumprint {\ratio}
\newcommand{\stuff}{\FPeval\result {(\x)*(\y)+(\x)^(\y)}}
% works also
\FPnumprint {\stuff}
\end{document}
输出:
顺便说一下,可以使用这种技术将所有命令转换fp.sty
为可嵌套的实体:(我使用 fFP@
前缀来表示fp 命令的功能形式)
\documentclass{article}
\usepackage{fp}
\usepackage{numprint}
% ORIGINAL \ratio
% by convention its result is in ... \result
\newcommand{\ratio}{%
\FPdiv\result{\x}{\y}%
\FPround\result\result{2}%
\result%
}
% WRAPPER to \numprint
% Must be applied to things like \ratio which compute a \result
\def\FPnumprint #1{%
\setbox0 \hbox{\def\result{#1}#1\expandafter}\expandafter
\numprint\expandafter {\result}}
\makeatletter
\def\fFPadd #1#2{%
\setbox0 \hbox{\def\fFP@result{#1}#1\expandafter}%
\expandafter\def\expandafter\fFP@tmpa\expandafter{\fFP@result}%
\setbox0 \hbox{\def\fFP@result{#2}#2\expandafter}%
\expandafter\def\expandafter\fFP@tmpb\expandafter{\fFP@result}%
\FPadd\fFP@result \fFP@tmpa\fFP@tmpb
\fFP@result
}
\def\fFPmul #1#2{%
\setbox0 \hbox{\def\fFP@result{#1}#1\expandafter}%
\expandafter\def\expandafter\fFP@tmpa\expandafter{\fFP@result}%
\setbox0 \hbox{\def\fFP@result{#2}#2\expandafter}%
\expandafter\def\expandafter\fFP@tmpb\expandafter{\fFP@result}%
\FPmul\fFP@result \fFP@tmpa\fFP@tmpb
\fFP@result
}
\def\fFPround #1#2{%
\setbox0 \hbox{\def\fFP@result{#1}#1\expandafter}%
\expandafter\def\expandafter\fFP@tmpa\expandafter{\fFP@result}%
\setbox0 \hbox{\def\fFP@result{#2}#2\expandafter}%
\expandafter\def\expandafter\fFP@tmpb\expandafter{\fFP@result}%
\FPround\fFP@tmpb\fFP@tmpb {0}%
\FPround\fFP@result \fFP@tmpa\fFP@tmpb
\fFP@result
}
\def\fFPnumprint #1{%
\setbox0 \hbox {\def\fFP@result {#1}#1\expandafter}%
\expandafter\numprint\expandafter{\fFP@result}%
}
\makeatother
\begin{document}
\FPset\x{2}
\FPset\y{3}
% works
\FPnumprint {\ratio}
\newcommand{\stuff}{\FPeval\result {(\x)*(\y)+(\x)^(\y)}}
% works also
\FPnumprint {\stuff}
\fFPadd {\fFPmul{2}{7}}{\fFPmul{3}{7}}
\fFPround {\fFPadd{\fFPmul {3.21267}{5.277282}}{16.8927287}}{2}
\fFPround {\fFPadd{\fFPmul {3.21267}{5.277282}}{16.8927287}}{\fFPadd {1}{3}}
% !!! ATTENTION !!!
% HERE We USE \fFPnumprint as we know the "result" is in "\fFP@result" not in
% "\result"
\fFPnumprint {\fFPround {\fFPadd{\fFPmul
{3.21267}{5.277282}}{16.8927287}}{\fFPadd {1}{3}}}
\end{document}
输出:
也许可以从这个起点出发制作一些包......因为它fp
很常用。
除了上述内容之外,还需要以下类型的宏:
\makeatletter
\def\fFPedef #1#2{%
\setbox0 \hbox{\def\fFP@result{#2}#2\expandafter}%
\expandafter\edef\expandafter#1\expandafter{\fFP@result}%
}
\makeatother
\ttfamily
\fFPedef\x {\fFPmul{3.142627627}{\fFPadd{6.75272872}{7.832298292}}}
\meaning\x
输出:
最大的区别是,正如我们在这里看到的,这些东西是可以嵌套的。