expl3 当被另一个宏调用时为空变量

expl3 当被另一个宏调用时为空变量

以下是我尝试做的精简版。如果某些计算不合理,请提前致歉,为了简单起见,我对其进行了修改。

基本上,我有一个名为的函数\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}

相关内容