我对 LaTeX 3 从函数返回值感到困惑

我对 LaTeX 3 从函数返回值感到困惑

以下代码按预期运行,并在终端上打印“答案是 5”。我如何更改以下代码,以便用伪代码替换行 B 和 C,

set \l_width_int to \width:N \l_test_seq

我在 A、B、C 线和其他地方尝试了很多方法,但都无法正常工作。我很困惑。谢谢。

\documentclass{article}
\usepackage[check-declarations]{expl3}
\begin{document}
\ExplSyntaxOn

  \int_new:N \l_width_int
  \seq_new:N \l_test_seq

  \seq_set_from_clist:Nn \l_test_seq {alpha,beta,gamma}

  % This function sets \l_width_int to the number of characters
  % in the longest element of the sequence argument.
  \cs_new:Npn \width:N #1
    {
      \int_new:N \l__width_curwidth_int
      \int_new:N \l__width_maxwidth_int
      \tl_new:N  \l__width_cur_tl

      \int_set:Nn    \l__width_maxwidth_int {-1}
      \seq_map_inline:Nn {#1}
        {
          \tl_set:Nn  \l__width_cur_tl {##1}
          \int_set:Nn \l__width_curwidth_int {\tl_count:N \l__width_cur_tl}
          \int_compare:nT
            {\l__width_curwidth_int > \l__width_maxwidth_int}
            {\int_set:Nn \l__width_maxwidth_int \l__width_curwidth_int}
        }
      \int_set:Nn \l_width_int \l__width_maxwidth_int    % line A
    }
  \width:N \l_test_seq                                   % line B
  \immediate\write16{answer~is~\int_use:N \l_width_int}  % line C

\ExplSyntaxOff
\end{document}

答案1

为了能够\l_width_int在 TeX 中将函数(实际上是宏)的结果设置为(或任何变量),我们必须安排通过扩展进行计算。换句话说expl3,这意味着您只能使用 中带有星号的函数interface3。为此,我们需要重新排列代码。我会选择类似

\cs_new:Npn \MS_width:N #1
  {
    \seq_map_function:NN {#1} \__MS_width:n
    \__MS_result:n { -1 }
  }
\cs_new:Npn \__MS_result:n #1 {#1}
\cs_new:Npn \__MS_width:n #1 #2 \__MS_result:n #3
  {
    \__MS_width:fnn
      { \int_eval:n { \tl_count:n {#1} } } {#2} {#3}
  }
\cs_new:Npn \__MS_width:nnn #1#2#3
  {
    \int_compare:nNnTF {#1} > {#3}
      { #2 \__MS_result:n {#1} }
      { #2 \__MS_result:n {#3} }
  }
\cs_generate_variant:Nn \__MS_width:nnn { f }

这个想法是设置映射,使得结果始终保持在输入流中:我使用它\__MS_result:n作为标记来知道它在哪里,并允许映射标记“移动”。我还安排了一个简单的“不做任何事”函数,最终在映射结束时转储结果。这将在

\iow_term:x { answer~is~\MS_width:N \l_MS_test_seq }

(我在这里给你的公共序列起了一个‘正确’的名字。)

请注意,我已经使用 -type 扩展“强制”评估标记列表的长度f,因此该值是精确计算出来的,而不是每次都可能计算出来的(如果列表中的第一个项目是最长的)。


我在这里使用的事实是,我知道我可以使用序列映射“移动东西”。我不想依赖序列的特定内部形式,所以必须坚持\seq_map_function:NN。有人可能会说,直接从逗号列表中工作会更好,因此可以确切地知道所有代码中发生了什么:

\cs_new:Npn \MS_width_comma:N #1
  {
    \exp_after:wN \__MS_width:w #1 , \q_nil , \q_stop
      { -1 }
  }
\cs_new:Npn \__MS_width:w #1 , #2 \q_stop #3
  {
    \quark_if_nil:nTF {#1}
      { #3 }
      {
        \__MS_width:fnn { \int_eval:n { \tl_count:n {#1} } }
          {#2} {#3}
      }
  }
\cs_new:Npn \__MS_width:nnn #1#2#3
  {
    \int_compare:nNnTF {#1} > {#3}
      { \__MS_width:w #2 \q_stop {#1} }
      { \__MS_width:w #2 \q_stop {#3} }
  }
\cs_generate_variant:Nn \__MS_width:nnn { f }

基本思想当然是一样的:根据输入的性质,你可以选择任何一种方式。(我不清楚你是从用户逗号列表开始,还是有一个包含空项的序列,ETC。

答案2

你把事情弄得太复杂了。;-)

\documentclass{article}
\usepackage[check-declarations]{expl3}

\ExplSyntaxOn

\int_new:N \l_senn_width_int
\seq_new:N \l_senn_test_seq

\seq_set_from_clist:Nn \l_senn_test_seq {alpha,beta,gamma}

% This function sets \l_senn_width_int to the number of characters
% in the longest element of the sequence argument.
\cs_new_protected:Npn \senn_width:N #1
 {
  \int_set:Nn \l_senn_width_int {-1}
  \seq_map_inline:Nn #1
   {
    \int_set:Nn \l_senn_width_int
     {
      \int_max:nn { \l_senn_width_int } { \tl_count:n { ##1 } }
     }
   }
 }
\senn_width:N \l_senn_test_seq
\typeout{answer~is~\int_use:N \l_senn_width_int}

\ExplSyntaxOff

需要注意的几点:

  1. 使用前缀。

  2. 永远不要在函数中分配变量,除非函数专门设计用于分配变量未来用法。

  3. 您可以使用变量的当前值来设置变量。

  4. 该函数\tl_count:n可扩展,可以在整数环境中使用。

  5. 主要功能\senn_width:N应该是protected,因为它对变量进行赋值。


关于你的问题,比如

\int_set:Nn \l_senn_width_int { \senn_width:N \l_senn_test_seq }

需要\senn_width:N完全可扩展,但我认为这并不容易。

您可以做的是定义一个双参数宏:

\senn_width_set:NN \l_senn_width_int \l_senn_test_seq

因此克服了可扩展性问题:

\cs_new_protected:Npn \senn_width_set:NN #1 #2
 {
  \int_set:Nn #1 {-1}
  \seq_map_inline:Nn #2
   {
    \int_set:Nn #1
     {
      \int_max:nn { #1 } { \tl_count:n { ##1 } }
     }
   }
 }
\senn_width_set:NN \l_senn_width_int \l_senn_test_seq

相关内容