由于我现在通过宏访问表中的值,以便在文本中进行动态引用(请参阅这里) 并使用它进行计算,我需要一种方法来使存储值的结果变量更加用户友好。由于 LaTeX 的限制,不允许在宏名称中使用数字或任何分隔符(而我有很多宏),我使用\csname\dots\endcsname
TeX FAQ 中的“ ”方法来创建更有意义的名称。
这会导致以下复杂情况,如下面的 MWE 所示:我使用通过或siunitx
打印数字。由于这些命令仅接受数字,因此我无法在其中进行计算。我使用进行计算并将结果存储在(临时)名称下。这导致每个变量都有两行代码:\num
\SI
l3fp
\calc{\Numtest}{500/2}% store result
\DefineRemark{num:test}{\Numtest}% label result
在文本中,结果将被称为\num{\Remark{num:test}}
。我想将两个命令合并为一个命令,如下所示\newcalc{<varname>}{<calculation>}
。以下不起作用,因为\temp
不会被覆盖。我需要为每个 实例指定一个新的随机名称\newcalc
。
\newcommand{\newcalc}[2]{%
\calc{\temp}{#2}
\DefineRemark{#1}{\temp}
}
有什么方法可以解决这个问题?在 MWE 中的另一个注释中,我基于\num
并\SI
合并了\Remark
宏创建了两个新命令。通过完全复制原始定义来创建这些命令是个好主意吗?
\documentclass{scrartcl}
\usepackage{siunitx,xparse,expl3}
% simple calculation command that stores the result
\ExplSyntaxOn
\NewDocumentCommand {\calc} { m m } {
\tl_set:Nx #1 { \fp_to_tl:n {#2} }
}
\ExplSyntaxOff
% Workaround for non-letters in macro names
\newcommand{\DefineRemark}[2]{%
\expandafter\newcommand\csname rmk-#1\endcsname{#2}%
}
\newcommand{\Remark}[1]{\csname rmk-#1\endcsname}
% Create \Num and \NUM based on siunitx' \num and \SI to incorporate the above directly
% (so \Num is \num{\remark{.}}). Not sure if it as a good idea to do it that way
\ExplSyntaxOn
\NewDocumentCommand \Num { o m } {
\leavevmode
\group_begin:
\IfNoValueF {#1}
{ \keys_set:nn { siunitx } {#1} }
\__siunitx_number_output:n {\Remark{#2}}
\group_end:
}
\NewDocumentCommand \NUM { o m o m } {
\leavevmode
\group_begin:
\IfNoValueTF {#1}
{ \__siunitx_combined:nnnn { } {\Remark{#2}} {#3} {#4} }
{
\keys_set:nn { siunitx } {#1}
\__siunitx_combined:nnnn {#1} {\Remark{#2}} {#3} {#4}
}
\group_end:
}
\ExplSyntaxOff
% Test if \Num and \NUM Works
\calc{\Numtest}{500/2}
\DefineRemark{num:test}{\Numtest}
% Trying to combine \calc and \DefineRemark
% This does not work :( Would need to create a new "\temp" each time
\newcommand{\newcalc}[2]{%
\calc{\temp}{#2}
\DefineRemark{#1}{\temp}
}
\newcalc{new:calc1}{250/2}
\newcalc{new:calc2}{125/2}
\begin{document}
Test Num and NUM Macros: \Num{num:test} and \NUM{num:test}{\percent}
\vspace{2ex}
Test the combination of calc and DefineRemark. The two values should be
different: \Num{new:calc1} and \Num{new:calc2}
\end{document}
答案1
我建议采取一种截然不同的方法:
\documentclass{scrartcl}
\usepackage{siunitx,xparse,expl3}
% simple calculation command that stores the result
\ExplSyntaxOn
\NewDocumentCommand{\calc}{ m m }
{
\tl_set:cx { l_jorg_rmk_#1_tl } { \fp_to_tl:n { #2 } }
}
\NewDocumentCommand{\usekey}{ m }
{
\tl_use:c { l_jorg_rmk_#1_tl }
}
% Create \Num and \NUM based on siunitx' \num and \SI to incorporate the above directly
% (so \Num is \num{\remark{.}}). Not sure if it as a good idea to do it that way
\NewDocumentCommand \Num { o m }
{
\leavevmode
\group_begin:
\IfNoValueF {#1}
{ \keys_set:nn { siunitx } {#1} }
\__siunitx_number_output:n {\tl_use:c { l_jorg_rmk_#2_tl } }
\group_end:
}
\NewDocumentCommand \NUM { o m o m }
{
\leavevmode
\group_begin:
\IfNoValueTF {#1}
{ \__siunitx_combined:nnnn { } { \tl_use:c { l_jorg_rmk_#2_tl } } {#3} {#4} }
{
\keys_set:nn { siunitx } {#1}
\__siunitx_combined:nnnn {#1} { \tl_use:c { l_jorg_rmk_#2_tl } } {#3} {#4}
}
\group_end:
}
\ExplSyntaxOff
% Test if \Num and \NUM Works
\calc{num:test}{500/2}
\calc{new:calc1}{250/2}
\calc{new:calc2}{125/2}
\begin{document}
Test Num and NUM Macros: \Num{num:test} and \NUM{num:test}{\percent}
\vspace{2ex}
Test the combination of calc and DefineRemark. The two values should be
different: \Num{new:calc1} and \Num{new:calc2}
\vspace{2ex}
Here's a key used by itself: \usekey{num:test}
\end{document}
但是,使用\__siunitx_number_output:n
和\__siunitx_combined:nnnn
是一种不恰当的处理方式,因为这些命令以__
哪个为前缀使得它们成为“内部”的,因此必然会在未经通知的情况下进行更改(感谢 Bruno Le Floch 对此的评论)。
在我看来siunitx
应该为内部命令提供公共接口,但目前以这种方式使用命令存在很多风险。因此,更好的建议是将\Num
和的定义更改\NUM
为如下
\NewDocumentCommand{\Num} { o m }
{
\num [ #1 ] { \tl_use:c { l_jorg_rmk_#2_tl } }
}
\NewDocumentCommand{\NUM} { o m o m }
{
\SI [ #1 ] { \tl_use:c { l_jorg_rmk_#2_tl } } [ #3 ] { #4 }
}
\SI
这更简单。和的调用\num
应该自己考虑\IfNoValue...
业务。