2

Let's say that I want to iterate on the lines of a file and try to do a regex replace on each line, and do something with the outcome if there is a match, but there are in fact two regexes and I want to act (the same way) if either matches, avoiding code duplication. My current code looks like this:

\cs_new:Nn \vb_or:nnTF { #1 { #3 } { #2 { #3 } { #4 } } }

\ior_str_map_variable:NNn \g_tmpa_ior \l_tmpa_str 
    { \vb_or:nnTF 
        { \regex_replace_once:NnNTF \c_first_regex { foo } \l_tmpa_str }
        { \regex_replace_once:NnNTF \c_second_regex { bar } \l_tmpa_str }
        { \typeout{\l_tmpa_str} } 
        {}
    }

The utility function \vb_or:nnTF feels like it exists somewhere already, but on the other hand it is very brittle with respect to T, F, TF variants and I fear that it might rely on implementation details to work. OTOH setting booleans and so on is much more verbose. What would be the proper way?

5
  • normally you should have also a predicate version, and then you can do \bool_if:n {\vb_foo_p:n{whatever} || \vb_bar_p:n{something} } Dec 29 '20 at 16:04
  • Agreed if I am providing the macro. In my use case I actually use \regex_replace_once:NnNTF and \regex_replace_once:NnNp is not available Dec 29 '20 at 16:08
  • well improve your question. Obviously you forgot to mention quite some important details. Dec 29 '20 at 16:13
  • Since you're using unexpandable tests, is your question about a more robust version of \vb_or:nnTF? The restriction that it works only with TF variants seems fine to me (it looks like something you want to use as an internal anyway).
    – Skillmon
    Dec 29 '20 at 16:22
  • Edited to be closer to my actual use case. I'm fine with restricting to TF variants. Dec 29 '20 at 16:27
4

The canonical way would be to also provide a _p variant and combine them with the functions of the l3bool module, like so:

\documentclass[]{article}

\ExplSyntaxOn
\prg_new_conditional:Npnn \vb_foo:n #1 { p, TF, T, F }
  {
    \str_if_eq:nnTF { foo } {#1}
      \prg_return_true:
      \prg_return_false:
  }
\prg_new_conditional:Npnn \vb_bar:n #1 { p, TF, T, F }
  {
    \str_if_eq:nnTF { bar } {#1}
      \prg_return_true:
      \prg_return_false:
  }
\ExplSyntaxOff

\begin{document}
\ExplSyntaxOn
\bool_lazy_or:nnTF
  { \vb_foo_p:n { bar } }
  { \vb_bar_p:n { bar } }
  { True }
  { False }
\ExplSyntaxOff
\end{document}

Note that this is only possible with fully expandable tests (if your test isn't expandable, you can't create a working p variant, or use it in a bool-expression).

If you can't create an expandable version, I'd probably really provide a wrapper similar to yours (though I'd define it with \prg_new_protected_conditional:Npnn). So in that case I'd use something like the following:

\documentclass[]{article}

\ExplSyntaxOn

\prg_new_protected_conditional:Npnn \vb_protected_or:nn #1#2 { TF, T, F }
  {
    #1
      { \prg_return_true: }
      { #2 \prg_return_true: \prg_return_false: }
  }

\prg_new_conditional:Npnn \vb_foo:n #1 { p, TF, T, F }
  {
    \str_if_eq:nnTF { foo } {#1}
      \prg_return_true:
      \prg_return_false:
  }
\prg_new_conditional:Npnn \vb_bar:n #1 { p, TF, T, F }
  {
    \str_if_eq:nnTF { bar } {#1}
      \prg_return_true:
      \prg_return_false:
  }
\ExplSyntaxOff

\begin{document}
\ExplSyntaxOn
\bool_lazy_or:nnTF
  { \vb_foo_p:n { bar } }
  { \vb_bar_p:n { bar } }
  { True }
  { False }
\par
\vb_protected_or:nnTF
  { \vb_foo:nTF { bar } }
  { \vb_bar:nTF { bar } }
  { True }
  { False }
\ExplSyntaxOff
\end{document}

or use temporary boolean variables I'd set dependent on the result of your tests.

Also, in your code the second regex replacement isn't applied if the first matched. If the second should also always be used, I'd probably use a temporary boolean variable:

\bool_new:N \l_vb_tmpa_bool
\ior_str_map_variable:NNn \g_tmpa_ior \l_tmpa_str 
    {
      \bool_set_false:N \l_vb_tmpa_bool
      \regex_replace_once:NnNT \c_vb_first_regex { foo } \l_tmpa_str
        { \bool_set_true:N \l_vb_tmpa_bool }
      \regex_replace_once:NnNT \c_vb_second_regex { bar } \l_tmpa_str
        { \bool_set_true:N \l_vb_tmpa_bool }
      \bool_if:NT \l_vb_tmpa_bool
        { \typeout { \l_tmpa_str } }
    }
4
  • So if my \vb_foo:nTF is actually a library function taking several arguments and not providing the p variant, your advice would be to wrap it into a new conditional of my own with the p variant? I like it Dec 29 '20 at 16:11
  • @VincentBeffara if the library function is fully expandable, yes, but chances are that it isn't if no p variant is provided. You can see whether the library function is expandable if it has a star next to it.
    – Skillmon
    Dec 29 '20 at 16:15
  • Ah, I am beginning to understand. Question updated to more closely match my setup... Dec 29 '20 at 16:30
  • @Vincent modification of variables can never be expandable.
    – cgnieder
    Dec 29 '20 at 16:52

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.