![expl3 当被另一个宏调用时为空变量](https://linux22.com/image/441558/expl3%20%E5%BD%93%E8%A2%AB%E5%8F%A6%E4%B8%80%E4%B8%AA%E5%AE%8F%E8%B0%83%E7%94%A8%E6%97%B6%E4%B8%BA%E7%A9%BA%E5%8F%98%E9%87%8F.png)
以下是我尝试做的精简版。如果某些计算不合理,请提前致歉,为了简单起见,我对其进行了修改。
基本上,我有一个名为的函数\basicMacro
,它接受一个参数,并根据其值进行一些计算(浮点计算,即使在简化的示例中并非如此)。
我想创建另一个宏(\anotherUseMacro
),它将对输入变量进行一些修改,然后使用参数调用它\basicMacro
。
然而,第二步出现了问题,我最终得到一个空的()
浮点变量,我不知道为什么。
这是 MWE:
\documentclass{article}
\usepackage{expl3}
\ExplSyntaxOn
\fp_new:N \l_cvssAV_fp
\fp_new:N \l_argument_fp
\fp_new:N \l_value_fp
\fp_new:N \l_result_fp
\str_new:N \l_input_str
\NewDocumentCommand \chooseValue { m }{
\str_case:nn {#1}
{
{ N } { 0.85 } % This value will be selected
{ A } { 0.62 } % This will not
}
}
\NewExpandableDocumentCommand \computeValue {m}{
\fp_set:Nn \l_argument_fp { \chooseValue{#1} }
\fp_show:N \l_argument_fp % \l_argument_fp=(). <<<< HERE IS THE ERROR
\fp_set:Nn \l_value_fp { 1 - (1 - \l_argument_fp )}
% 1 - (1 - 0.85 )
}
\NewDocumentCommand \factorScaling {m}{%
\fp_eval:n { 8.22 * (#1) }%
% 8.22 * 0.85
}%
% https://tex.stackexchange.com/a/615358/28926
\NewDocumentCommand \roundup {m}{
\fp_eval:n { ceil(#1,1) }
\fp_compare:nT { ceil(#1,1)=ceil(#1,0) } {.0}
}
\cs_new_protected:Npn \basicMacro #1 {
\computeValue{#1}
% \l_argument_fp=0.85
\fp_set:Nn \l_result_fp { \factorScaling{\chooseValue{#1}} }%
% \l_result_fp=8.22*0.85
% \l_result_fp=6.987
%
\fp_compare:nTF { \l_value_fp <= 0 }
% IF value <=0
{
% value <=0
0.0
}{
\fp_eval:n { round( min( 1.08 * (\l_value_fp + \l_result_fp), 10) ) }%
}%
}
\NewDocumentCommand \anotherUseMacro { m }{%
\str_set:Nn \l_input_str { #1 }
% CALLS basicMACRO
\basicMacro{\l_input_str}
}%
\ExplSyntaxOff
\begin{document}
\basicMacro{N}
\anotherUseMacro{N}
\end{document}
查看日志,我可以看到变量\l_argument_fp
在第一次调用中正确等于 0.85,但在第二种情况下(在内部使用时\anotherUseMacro
),它为空:
l.69 \basicMacro{N}
> \l_argument_fp=().
<recently read> }
因此,下一个操作无效,因此抛出错误:
Invalid operation (1)-(())
我该如何解决我的问题?似乎\fp_set:Nn \l_argument_fp { \chooseValue{#1} }
从 调用表达式时未正确执行\anotherUserMacro
。
注意:我对 expl3 经验不多。
答案1
你\basicMacro
这样定义它需要一个单个字符作为参数,然后\chooseValue
执行:
\str_case:nn {#1}
{
{ N } { 0.85 } % This value will be selected
{ A } { 0.62 } % This will not
}
选择一个。但是当您这样做时,在\anotherUseMacro
:
\str_set:Nn \l_input_str { #1 }
\basicMacro{\l_input_str}
你实际上是\chooseValue
在调用细绳 \l_input_str
, 和不是及其值。如果你改变 的定义,\chooseValue
在调用中添加一个默认情况,你就能很容易地发现问题\str_case:nn
:
\NewDocumentCommand \chooseValue { m }
{
\str_case:nnF {#1}
{
{ N } { 0.85 } % This value will be selected
{ A } { 0.62 } % This will not
}
{
\msg_expandable_error:nnn { 3isenheim } { wrong-case } {#1}
1.00 % default value
}
}
\msg_new:nnn { 3isenheim } { wrong-case }
{ Value~'#1'~invalid~for~\iow_char:N\\chooseValue. }
然后你会看到错误信息:
! Use of \??? doesn't match its definition.
<argument> \???
! Package 3isenheim Error: Value '\l_input_str ' invalid fo...
l.79 \anotherUseMacro{N}
?
您必须先扩展字符串变量,然后再进行比较。一种选择是始终彻底扩展参数,然后可以使用\str_case_e:nnF
而不是\str_case:nnF
(在上面的定义中)。另一种方法是先扩展 ,\l_input_str
然后\anotherUseMacro
再将其传递给\basicMacro
:
\NewDocumentCommand \anotherUseMacro { m }
{
\str_set:Nn \l_input_str {#1}
\exp_args:NV \basicMacro \l_input_str
}
其他要点:
你的变量应该被调用
\l__eisenheim_input_str
等等,以便有一个适当的<module>
部分。有一个解释这里;\chooseValue
看起来像一个内部宏,所以我会调用它\__eisenheim_choose_value:n
并用它定义它\cs_new:Npn
;\computeValue
是不可扩展的(因为\fp_set:Nn
是不可扩展的)所以您应该用来定义它\NewDocumentCommand
(或者\cs_new_protected:Npn
如果它是内部的)。
完整代码:
\documentclass{article}
\usepackage{expl3}
\ExplSyntaxOn
\fp_new:N \l_cvssAV_fp
\fp_new:N \l_argument_fp
\fp_new:N \l_value_fp
\fp_new:N \l_result_fp
\str_new:N \l_input_str
\NewDocumentCommand \chooseValue { m }
{
\str_case:nnF {#1}
{
{ N } { 0.85 } % This value will be selected
{ A } { 0.62 } % This will not
}
{
\msg_expandable_error:nnn { 3isenheim } { wrong-case } {#1}
1.00 % default value
}
}
\msg_new:nnn { 3isenheim } { wrong-case }
{ Value~'#1'~invalid~for~\iow_char:N\\chooseValue. }
\NewDocumentCommand \computeValue {m}{
\fp_set:Nn \l_argument_fp { \chooseValue{#1} }
\fp_show:N \l_argument_fp % \l_argument_fp=(). <<<< HERE IS THE ERROR
\fp_set:Nn \l_value_fp { 1 - (1 - \l_argument_fp )}
% 1 - (1 - 0.85 )
}
\NewDocumentCommand \factorScaling {m}{%
\fp_eval:n { 8.22 * (#1) }%
% 8.22 * 0.85
}%
% https://tex.stackexchange.com/a/615358/28926
\NewDocumentCommand \roundup {m}{
\fp_eval:n { ceil(#1,1) }
\fp_compare:nT { ceil(#1,1)=ceil(#1,0) } {.0}
}
\cs_new_protected:Npn \basicMacro #1 {
\computeValue{#1}
% \l_argument_fp=0.85
\fp_set:Nn \l_result_fp { \factorScaling{\chooseValue{#1}} }%
% \l_result_fp=8.22*0.85
% \l_result_fp=6.987
%
\fp_compare:nTF { \l_value_fp <= 0 }
% IF value <=0
{
% value <=0
0.0
}{
\fp_eval:n { round( min( 1.08 * (\l_value_fp + \l_result_fp), 10) ) }%
}%
}
\NewDocumentCommand \anotherUseMacro { m }{%
\str_set:Nn \l_input_str { #1 }
% CALLS basicMACRO
\exp_args:NV \basicMacro \l_input_str
}%
\ExplSyntaxOff
\begin{document}
\basicMacro{N}
\anotherUseMacro{N}
\end{document}
这是 的完全可扩展实现\basicMacro
。由于您基本上只进行浮点计算,因此很容易做到这一点。大致来说,您想替换每次出现的:
\cs_new:Npn \some_macro:n #1
{
\fp_set:Nn \l_some_temp_fp { <computations> }
\fp_eval:n { <more computations with \l_some_temp_fp> }
}
经过
\cs_new:Npn \some_macro:n #1
{
\exp_args:Ne \__some_macro_internal:n
{ \fp_eval:n { <computations> } }
}
\cs_new:Npn \__some_macro_internal:n #1
{ \fp_eval:n { <more computations with #1> } }
这样您就可以删除不可扩展的\fp_set:Nn
,而只需使用\fp_eval:n
(可扩展)并将其结果作为参数(可扩展)传递。
完整代码如下:
\documentclass{article}
\usepackage{expl3}
\ExplSyntaxOn
\cs_new:Npn \__eisenheim_choose_value:n #1
{
\str_case:nnF {#1}
{
{ N } { 0.85 } % This value will be selected
{ A } { 0.62 } % This will not
}
{
\msg_expandable_error:nnn { 3isenheim } { wrong-case } {#1}
1.00 % default value
}
}
\msg_new:nnn { 3isenheim } { wrong-case }
{ Value~'#1'~invalid~for~\iow_char:N\\chooseValue. }
\cs_new:Npn \__eisenheim_compute_value:n #1 { 1 - (1 - \__eisenheim_choose_value:n {#1} ) }
\cs_new:Npn \__eisenheim_factor_scaling:n #1 { 8.22 * (#1) }
\NewExpandableDocumentCommand \basicMacro { m }
{
\exp_args:Nee \__eisenheim_basic_aux:nn
{ \fp_eval:n { \__eisenheim_compute_value:n {#1} } }
{ \fp_eval:n { \__eisenheim_factor_scaling:n { \__eisenheim_choose_value:n {#1} } } }
}
\cs_new:Npn \__eisenheim_basic_aux:nn #1 #2
{
\fp_compare:nTF { #1 <= 0 }
{ 0.0 }
{ \fp_eval:n { round( min( 1.08 * (#1 + #2), 10) ) } }
}
\ExplSyntaxOff
\begin{document}
\edef\x{\basicMacro{N}}
\texttt{\meaning\x}
\end{document}
答案2
您必须访问字符串的值,而不是容器的值。
这是您的代码的修改版本,其中的内部函数根据推荐的样式命名,并且对可扩展函数和受保护函数有更精确的区分(您将它们混淆了)。
\documentclass{article}
\usepackage{expl3}
\ExplSyntaxOn
\fp_new:N \l_eisenheim_cvssAV_fp
\fp_new:N \l_eisenheim_argument_fp
\fp_new:N \l_eisenheim_value_fp
\fp_new:N \l_eisenheim_result_fp
\str_new:N \l_eisenheim_input_str
\cs_new:Nn \eisenheim_choosevalue:n
{
\str_case:nn {#1}
{
{ N } { 0.85 } % This value will be selected
{ A } { 0.62 } % This will not
}
}
\cs_new_protected:Nn \eisenheim_computevalue:n
{
\fp_set:Nn \l_eisenheim_argument_fp { \eisenheim_choosevalue:n {#1} }
\fp_show:N \l_eisenheim_argument_fp % \l_eisenheim_argument_fp=(). <<<< HERE IS THE ERROR
\fp_set:Nn \l_eisenheim_value_fp { 1 - (1 - \l_eisenheim_argument_fp )}
% 1 - (1 - 0.85 )
}
\cs_new:Nn \eisenheim_factorscaling:n
{
\fp_eval:n { 8.22 * (#1) }%
% 8.22 * 0.85
}
% https://tex.stackexchange.com/a/615358/28926
\cs_new:Nn \eisenheim_roundup:n
{
\fp_eval:n { ceil(#1,1) }
\fp_compare:nT { ceil(#1,1)=ceil(#1,0) } {.0}
}
\NewDocumentCommand{\basicMacro} {m}
{
\eisenheim_basicmacro:n {#1}
}
\cs_new_protected:Nn \eisenheim_basicmacro:n
{
\eisenheim_computevalue:n {#1}
% \l_eisenheim_argument_fp=0.85
\fp_set:Nn \l_eisenheim_result_fp
{
\eisenheim_factorscaling:n { \eisenheim_choosevalue:n {#1} }
}
% \l_eisenheim_result_fp=8.22*0.85
% \l_eisenheim_result_fp=6.987
%
\fp_compare:nTF { \l_eisenheim_value_fp <= 0 }
% IF value <=0
{
% value <=0
0.0
}
{
\fp_eval:n { round( min( 1.08 * (\l_eisenheim_value_fp + \l_eisenheim_result_fp), 10) ) }
}
}
\cs_generate_variant:Nn \eisenheim_basicmacro:n { V }
\NewDocumentCommand \anotherUseMacro { m }
{
\str_set:Nn \l_eisenheim_input_str { #1 }
% CALLS basicMACRO
\eisenheim_basicmacro:V \l_eisenheim_input_str
}
\ExplSyntaxOff
\begin{document}
\basicMacro{N}
\anotherUseMacro{N}
\end{document}