0

If \myB accepts two optional arguments, \myB sets both of them to default values, \myB[5] sets first to 5 and second to default value, \myB[5][10] sets first to 5 and second to 10. But how do we set #2 argument only? Doing \myB[][10] does NOT set first argument to default value (it just makes it disappear).

\documentclass{standalone}
\usepackage{xparse}
% each of two optional arguments defaults to zero
\NewDocumentCommand{\myB}{O{0}O{0}}
  {%
    myB: #1, #2%
  }
\begin{document}
% how to call `myB` so that it only has #2 set? (not #1)
\myB[][6] % this is no good since `[]` removes default zero
\end{document}
  • 1
    @bp2017 you don't need it here, but if you turn \ExplSyntaxOn a normal space is ignored and ~ will be a normal space instead. I guess he wrote that out of habit (or to be more precise, you'll need \ExplSyntaxOn to use \tl_if_empty:nTF). – Skillmon May 12 at 18:29
  • 1
    @Skillmon I thought of that. But \IfNoValueTF checks only for -NoValue-. It won't work for [][6] because #1 will be empty. – Phelype Oleinik May 12 at 18:30
  • 1
    @bp2017 normally you could use processors, but because of reasons not understandable to myself, the -NoValue- flag wouldn't ever be passed to an argument processor. – Skillmon May 12 at 18:36
  • 2
    @Skillmon Did you ask for questionable syntax?! Define \NewDocumentCommand{\myB}{E{12}{{0}{0}}}{myB: #1, #2} and use \myB2{6}. Problem. Solved. :D – Phelype Oleinik May 12 at 18:41
  • 1
    @bp2017 if you meant the latter, I might do this with argument processors as well: \cs_new_protected:Npn \__bdmmxvii_Oarg_pass_proc:n #1 { \tl_if_empty:nTF { #1 } { \cs_set:Npn \ProcessedArgument {} } { \cs_set:Npn \ProcessedArgument { [ { #1 } ] } } } \NewDocumentCommand \foo { >{ \__bdmmxvii_Oarg_pass_proc:n } O{} O{0} } { \baz #1 { #2 } } if the first argument of \baz is optional and the second mandatory. – Skillmon May 12 at 19:10
3

As Phelype Oleinik suggested you could use an empty first optional argument and assign the default if it is empty. I'd do that with an argument processor:

\ExplSyntaxOn
\cs_new_protected:Npn \__bdmmxvii_Oarg_proc:nn #1 #2
  {
    \tl_if_empty:nTF { #2 }
      { \cs_set:Npn \ProcessedArgument { #1 } }
      { \cs_set:Npn \ProcessedArgument { #2 } }
  }
\NewDocumentCommand \twooptsA { >{ \__bdmmxvii_Oarg_proc:nn { 0 } }O{} O{0} }
  {
    myB:~#1,~#2
  }
\ExplSyntaxOff

Alternatively my suggestion with the star:

\ExplSyntaxOn
\NewDocumentCommand \twooptsB { O{0} s O{0} }
  {
    myB:~#1,~#3
  }
\ExplSyntaxOff

And as a third option a key=value syntax:

\ExplSyntaxOn
\keys_define:nn { bdmmxvii }
  {
    ,1 .tl_set:N  = \__bdmmxvii_arg_a_tl
    ,1 .initial:n = 0
    ,2 .tl_set:N  = \__bdmmxvii_arg_b_tl
    ,2 .initial:n = 0
  }
\NewDocumentCommand \twooptsC { O{} }
  {
    \group_begin:
    \keys_set:nn { bdmmxvii } { #1 }
    \__bdmmxvii_twooptsC:VV \__bdmmxvii_arg_a_tl \__bdmmxvii_arg_b_tl
    \group_end:
  }
\cs_new:Npn \__bdmmxvii_twooptsC:nn #1 #2
  {
    myB:~#1,~#2
  }
\cs_generate_variant:Nn \__bdmmxvii_twooptsC:nn { VV }
\ExplSyntaxOff

Everything together in a single document:

\documentclass[]{article}

\usepackage{xparse}

\ExplSyntaxOn
\cs_new_protected:Npn \__bdmmxvii_Oarg_proc:nn #1 #2
  {
    \tl_if_empty:nTF { #2 }
      { \cs_set:Npn \ProcessedArgument { #1 } }
      { \cs_set:Npn \ProcessedArgument { #2 } }
  }
\NewDocumentCommand \twooptsA { >{ \__bdmmxvii_Oarg_proc:nn { 0 } }O{} O{0} }
  {
    myB:~#1,~#2
  }
\NewDocumentCommand \twooptsB { O{0} s O{0} }
  {
    myB:~#1,~#3
  }
\keys_define:nn { bdmmxvii }
  {
    ,1 .tl_set:N  = \__bdmmxvii_arg_a_tl
    ,1 .initial:n = 0
    ,2 .tl_set:N  = \__bdmmxvii_arg_b_tl
    ,2 .initial:n = 0
  }
\NewDocumentCommand \twooptsC { O{} }
  {
    \group_begin:
    \keys_set:nn { bdmmxvii } { #1 }
    \__bdmmxvii_twooptsC:VV \__bdmmxvii_arg_a_tl \__bdmmxvii_arg_b_tl
    \group_end:
  }
\cs_new:Npn \__bdmmxvii_twooptsC:nn #1 #2
  {
    myB:~#1,~#2
  }
\cs_generate_variant:Nn \__bdmmxvii_twooptsC:nn { VV }
\ExplSyntaxOff

\begin{document}
\twooptsA\ \twooptsA[1][2] \twooptsA[1] \twooptsA[][2]

\twooptsB\ \twooptsB[1][2] \twooptsB[1] \twooptsB*[2]

\twooptsC\ \twooptsC[1=1,2=2] \twooptsC[1=1] \twooptsC[2=2]
\end{document}

Everything results in the same:

enter image description here

  • 1
    You forgot the E argument :( – Phelype Oleinik May 12 at 18:56
  • @PhelypeOleinik you might go for it, I don't like E types very much (but they are so powerful, why is it considered questionable syntax?) and they still are experimental. – Skillmon May 12 at 18:58
  • Just kidding ;) The E type, even though experimental, seems very robust, and is not considered questionable. How one use it, on the other hand... – Phelype Oleinik May 12 at 19:13
  • I like the \twooptsC syntax best but only when command is called, when it's defined, however... (too many directives, not to mention I need to learn what they are). – bp2017 May 12 at 19:16
  • 1
    @bp2017 I used \ExplSyntaxOn on all of them because they all were in that regime in my full document. Also the meaning of ~ would change and you'd have to make sure not to introduce parasitic spaces. – Skillmon May 12 at 19:44
2

This is bad syntax and you should consider to avoid it.

Anyway, you can preprocess the argument. Of course, you cannot set the first optional argument to empty.

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn
\NewDocumentCommand{\IfEmpty}{mm}
 {
  \tl_if_blank:nTF { #2 }
   { \tl_set:Nn \ProcessedArgument { #1 } }
   { \tl_set:Nn \ProcessedArgument { #2 } }
 }
\ExplSyntaxOff

\NewDocumentCommand{\myB}{>{\IfEmpty{0}}O{0}O{0}}{%
  \#1 is #1 -- \#2 is #2
}

\begin{document}

\myB

\myB[1]

\myB[1][2]

\myB[][3]

\end{document}

enter image description here

  • Why is it a bad syntax? – bp2017 May 13 at 0:11
  • 1
    @bp2017 Because you are specifying the first optional argument when you don't want to set a value for it. – egreg May 13 at 6:16
  • This is (almost) exactly the same as my first code block in my answer. – Skillmon May 13 at 6:50

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.