在 expl3 中提前中断 do/while/until 循环

在 expl3 中提前中断 do/while/until 循环

expl3提供了几个 do/while/repeat 循环,但我无法找到尽早打破此类循环的方法。

我想要的是类似的东西(用 C 风格的代码)

while (true) {
    if (<condition_1>)
        break;
    <some code>

    if (<condition_2>)
        break;
    <some other code>

    ...
    if (<condition_n>)
        break;
    <even more code>
}

我当然可以为此定义自己的宏,但是有没有办法使用expl3模块已经提供的函数?或者,expl3这方面的惯用解决方案是什么?\prg_break_point:看起来很有希望,但我不知道应该如何使用它。

答案1

概念证明,无需认真对待

你可以调皮一下,利用你想使用的函数的实现细节来更快地破坏它。例如,take 的\bool_do_while:Nn定义如下:

\cs_new:Npn \bool_do_while:Nn #1#2
  { #2 \bool_if:NT #1 { \bool_do_while:Nn #1 {#2} } }

您提供的代码在 之前执行\bool_if:NT。您可以定义一些宏,将其反转\bool_if:NT\bool_if:NF然后返回相反的(或者一个消耗一切并一举结束一切的宏)。例如(我希望这个名字足够有启发性 ;-)

\cs_new:Npn \__siracusa_DONT_DO_THIS:w #1 \bool_if:NT { \bool_if:NF }

那么你可以像这样使用:

\int_new:N \l_siracusa_tmp_int
\bool_do_while:Nn \c_true_bool
  {
    \int_incr:N \l_siracusa_tmp_int
    % if (<condition>)
    \int_compare:nNnT { \l_siracusa_tmp_int } = { 6 }
      { \__siracusa_DONT_DO_THIS:w } % break;
    % <some code>
    Hello~\int_use:N \l_siracusa_tmp_int \par
  }

您可以在循环主体中设置任意多个条件,并且只要\bool_if:NT代码主体中没有出现(至少没有括号)就可以从任何地方发出中断代码。例如,可以通过在循环主体末尾添加标记来使其更加健壮。

然而,这只适用于\bool_do_while:Nn,因为它依赖于宏的定义方式,并且不言而喻,如果实现中发生任何变化(这是可能的),代码就会严重崩溃。

答案2

需要说明的是,这是我目前使用的方法:

\cs_new_protected:Npn \__foo_goto_loop_end:w #1 \__foo_loop_end: { }
\cs_new_protected:Npn \__foo_loop_end: { }

\bool_set_true:N \l_tmpa_bool
\int_set:Nn \l_tmpa_int { 1 }
\bool_do_while:Nn \l_tmpa_bool {
    \int_compare:nNnT \l_tmpa_int > 9 {
        \bool_set_false:N \l_tmpa_bool
        \__foo_goto_loop_end:w
    }

    % Other conditions ...
    \int_compare:nNnT 0 = 1 {
        \bool_set_false:N \l_tmpa_bool
        \__foo_goto_loop_end:w
    }

    \iow_term:x { iteration~\int_eval:n \l_tmpa_int }
    \int_gincr:N \l_tmpa_int

    \__foo_loop_end:
}

代码跳过的思路类似于Phelype Oleinik 的回答,但它并没有真正打破循环,它只是跳到循环末尾,跳过循环体的所有剩余代码。因此,这不依赖于循环宏的内部定义,因此适用于所有循环变体。

相关内容