2

Question

Is it possible to get and store a count of the optional arguments actually passed to an xparse declare argument/environment?

Purpose

Regardless of the number of arguments provided (arbitrary depending on situation), I want to do something very specific with the last one given. Because this is a variable number, I need to retrieve the count of arguments given and use that combined with # to do something with that variable.

Code

\documentclass{article}
\usepackage{fontspec}
\usepackage{xparse}

\DeclareDocumentEnvironment{myenv}{O{}O{}O{}O{}O{}O{}O{}O{}O{}O{}} % 10 optional args
{startcode}
{endcode}

\DeclareDocumentCommand{\mycom}{O{}O{}O{}O{}O{}O{}O{}O{}O{}O{}}{% 10 optional args
}%

\begin{document}

\mycom[a][b][c] % Count 3
% Do something special with #3

\begin{myenv}[a][b][c][d] % Count 4
% Do something special with #4
\end{myenv}

\end{document}
  • 1
    Simply check from the end if the optional argument has no value. But are you sure that a command with so many arguments is senseful? I avoid more then two arguments. – Ulrike Fischer May 06 '15 at 09:21
  • @UlrikeFischer That test is not robust enough. It does not work when working with an arbitrary number of arguments. – Jonathan Komar May 06 '15 at 09:26
  • How can the number be arbitrary in the definition? Your example shows 10 arguments, so this is the maximum. – Ulrike Fischer May 06 '15 at 09:33
  • 3
    @macmadness86 You can't have more than nine arguments (like in all TeX macros). In any case, since the arguments would start from the beginning \IfValueTF{#9}{9 arguments}{\IfValueTF{#8}{8 arguments}{..}} would work. By the way, why not declare the last one as m mandatory, and the eight before as o? \foo[a][b][c][d]{e}. – Manuel May 06 '15 at 09:51
  • @Manuel I knew about the \IfValueTF but I was hoping for a "cleaner" solution (less code). I think just making the ninth argument mandatory is a good idea and should work for my purposes! I did not know about the maximum number of arguments. – Jonathan Komar May 06 '15 at 10:45
  • @Manuel Ok oops, the answer to your question about just declaring the last arg m is that I still do not know whether m=#1,#2,#3,#4,#5,#6,#7,#8, or #9. – Jonathan Komar May 06 '15 at 10:52
  • 1
    @macmadness86 If it's mandatory, it's the last one, so you know what argument you have to do the special thing to. I'm not really sure what your intentions are: surely this syntax is very clumsy and error prone. – egreg May 06 '15 at 11:38
  • @egreg Please consider my last comment. to Manuel. How do I know which number represents the last variable if the user can sometimes use 3 args (last=#3), sometimes 8 (last=#8)? Thats a pretty general judgement right there (about this syntax being clumsy), by the way. – Jonathan Komar May 06 '15 at 11:50
  • 1
    using more than two optional arguments is a horrible user interface, can you not use a comma separated list? \foo[a,b,c,d,e,f] expl3 then makes it easy to count the list, iterate over it, select the last etc. – David Carlisle May 06 '15 at 12:04
  • Isn't this all subject that xparse (or even the traditional way) can't know which of 1,2,3,4,5,6,7,8,9 optional arguments is really the one under supervision? Does \foo[This][is] mean This is the 1st or may be the 5th opt. argument? –  May 06 '15 at 12:04
  • @DavidCarlisle Putting aside that this doesn't really answer the question of getting an argument count, I do appreciate that you're trying to help me create a more efficient way of doing this. The actual use is to create a specially formatted form. I need to isolate the arguments to insert them in specific places. Another criterion is to keep the code readable. Being able to put argument inputs on separate lines is key. The other option is to create horrible looking tables of this information (I am trying to provide a better option) – Jonathan Komar May 06 '15 at 12:09
  • 1
    sure but [][][] is the wrong input syntax for that (irrespective of coding and efficiency issues) – David Carlisle May 06 '15 at 12:30
  • xparse is meant for defining LaTeX2e-like interface: it's not a general 'parse some input' set up. I wonder if you might be better with a delimited argument, so \foo a b c d e\stop or similar (where \stop may be defined naturally by your real use case). – Joseph Wright May 06 '15 at 12:34
  • @macmadness86 What do you mean? If you have {oooooooom} you ALWAYS have #9 as the mandatory argument. If you use \foo[a]{b} then #1 is a and #9 is b (the rest are “empty”). If you have \foo[a][b][c][d]{e} then #1#4 are ad and #9 is e (the rest are “empty”). I don't see what's the problem. In any case, I agree, this is not the optimal solution (more than 2 or 4 arguments is really a bad sign for a “user macro”). – Manuel May 06 '15 at 13:01
  • @Manuel Ah I did not know that #9 would always be the mandatory argument. Thank you for that information. – Jonathan Komar May 06 '15 at 13:23
  • 1
    Not sure why the question was closed as unclear. It's arguable if the requested syntax is ideal, but the actual question seems clear to me. – David Carlisle May 06 '15 at 14:30

1 Answers1

4

You shouldn't use repeated [] arguments in this way. use a comma separated list for an arbitrary argument syntax.

enter image description here

\documentclass{article}

\usepackage{xparse,expl3}

\ExplSyntaxOn
\DeclareDocumentEnvironment{myenv}{O{}} 
{\doargs:n{#1}}
{}

\DeclareDocumentCommand{\mycom}{O{}}{
\doargs:n{#1}
}%
\clist_new:N\arglist

\cs_new_protected:Nn\doargs:n{{
\clist_set:Nn\arglist{#1}
[there ~are ~\clist_count:N\arglist{} ~ arguments]
[the ~last ~one ~is ~\clist_item:Nn\arglist{\clist_count:N\arglist}]
}}
\ExplSyntaxOff

\begin{document}

\mycom[a,b,c] % Count 3
% Do something special with #3

\begin{myenv}[a,b,c,d] % Count 4
% Do something special with #4
\end{myenv}

\end{document}
Joseph Wright
  • 259,911
  • 34
  • 706
  • 1,036
David Carlisle
  • 757,742
  • Thank you for the excellent answer. Do you think you should show me how to iterate the parameters. e.g. My idea: user command variant and turn this: \noindent\clist_item:Nn\arglist{1},~\clist_item:Nn\arglist{2},~\clist_item:Nn\arglist{3},~\clist_item:Nn\arglist{4}\par into a loop. Each item can be put into a tabular using the trick you showed me \tabular{l{\arglist{\clist_count:N\arglist}}} (num of cols determined by clist_count). Each item (\clist_item:Nn\arglist{itemStep}) can be followed by & until column step=clist_count:N\arglist then a \\ is inserted. +1Automation – Jonathan Komar May 07 '15 at 08:38
  • 1
    @macmadness86 That's a new question really, but see texdoc interface3 from page 120 there are 10 pages of interesting functions you can apply to clists. – David Carlisle May 07 '15 at 08:48
  • +1 Thanks for the tip. I will hop to it. Just to clarify, this code with the ExplSyntaxOn/Off is fully backwards compatible? – Jonathan Komar May 07 '15 at 08:52
  • @macmadness86 backwards compatible with what? the only code there is the definition of a new command. – David Carlisle May 07 '15 at 10:07
  • I meant, can I compile this document on a machine with an older version of LaTeX, just by installing the expl3 package. I think I already found my answer though, and the answer is yes. – Jonathan Komar May 14 '15 at 07:18