Commands that may take a variable number of argumentsA command with a variable number of arguments: comma...

How to draw lines on a tikz-cd diagram

How to write papers efficiently when English isn't my first language?

How to be diplomatic in refusing to write code that breaches the privacy of our users

Opposite of a diet

India just shot down a satellite from the ground. At what altitude range is the resulting debris field?

Risk of infection at the gym?

Lay out the Carpet

Proof of work - lottery approach

Type int? vs type int

Is this apparent Class Action settlement a spam message?

Why didn't Theresa May consult with Parliament before negotiating a deal with the EU?

Is a stroke of luck acceptable after a series of unfavorable events?

How does it work when somebody invests in my business?

How to safely derail a train during transit?

How does Loki do this?

What happens if you roll doubles 3 times then land on "Go to jail?"

Detecting if an element is found inside a container

Is HostGator storing my password in plaintext?

Why are there no referendums in the US?

How does buying out courses with grant money work?

How does the UK government determine the size of a mandate?

Increase performance creating Mandelbrot set in python

How easy is it to start Magic from scratch?

How to check is there any negative term in a large list?



Commands that may take a variable number of arguments


A command with a variable number of arguments: comma versus andHow to print `n` arguments in a command?Cunning (La)TeX tricksUnderstanding @ifnextcharHow to create new commands with multiple arguments?Aligning tikz nodes to tabular cells?Expl3 syntax: graceful handing of conditional/undefined argumentsconvert macro from preamble into a cls fileHow to add numbers with character?DeclareMathOperator won't take argumentsCommand with variable number of arguments to format menu sequencesnewcommand with variable number of argumentsUsing mathpalette with macros that take more argumentsDefining a macro with a variable number of argumentsVariable number of arguments for newcommandLaTeX newcommand with a variable number of argumentsVariable number of arguments in a commanddefine variable with multiple argumentsVariable arguments with `@ifnextchar`













15















I have a situation where I want to define a command that takes a variable number of arguments, where the number of arguments is known programmatically via a count, and process the parameters in some way (say as if they are a list).



As an example, say I'd like to output the parameters as a comma-separated list.



newcommand{makecsv}[N]{#1, #2, ..., #N}


The code that I've come up with to do this kind of operation (in a generic-ish way) essentially takes a command, csv and expands it recursively N times. csv needs to know how to continue the recursion, and has some state that I'd like to thread through the recursion (rather than using global).



documentclass{report}
usepackage{etoolbox}

makeatletter
newcommand{ifzero}[3]{%
% #1: count
% #2: state for #3
% #3: macro to expand to
% - should take at least 2 parameters
% - ##1: count threaded through
% - ##2: macro state threaded through
ifnumc > 0
deftmp@f##1##2##3{##1{##2}{##3}}%
advance#1 -1%
else
deftmp@f##1##2##3{)}% note closeparen here (could be param)
fi
tmp@f{#3}{#1}{#2}%
}
makeatother

newcommand{csv}[3]{
% #1: count
% #2: separator state
% #3: string to concat
%
#2#3ifzero{#1}{, }{csv}%
}

newcommand{makecsv}[1]{%
ifzero{#1}{}{csv}%
}

makeatletter
newcommand{decl}[3]{%
% #1: decl id
% #2: decl symbol
% #3: # params
csgdef{decl@#1}{#2}%
globalexpandafternewcountcsname decl@#1@nparamsendcsname%
csuse{decl@#1@nparams} #3relax%
}

newcommand{usedecl}[1]{%
newcountc
c thecsuse{decl@#1@nparams}
csuse{decl@#1}(makecsv{c}%
}
makeatother

% declare some interface routines
decl{foo}{FOO}{3}
decl{bar}{BAR}{4}

begin{document}
usedecl{foo}{p1}{p2}{p3}par
usedecl{bar}{p1}{p2}{p3}{p4}par
end{document}


Is this a reasonable thing to do in 2e, or is there some sort of standard approach to this that is normally used?



Edit 1



It seems like my original MWE wasn't adequate to describe why someone might want this. I've updated the MWE with a use case. decl allows authors to declaratively define a C-style function, and usedecl allows the author to generate a use of the function, with its parameters bound to specific arguments.



This is similar enough to what I'm doing that it should help motivate the example.










share|improve this question

























  • You are asking for a list. I think you can search TeX.SX to find a lot of examples. Related packages are etoolbox or l3clist.

    – Marco Daniel
    Jun 7 '13 at 16:42











  • @Marco, I don't think that I'm asking for a list directly. I'm more interested in the technique for writing a command that can process a variable number of arguments. I can't control the source directly, so I can't change {foo}{bar}{foobar} to {foo|bar|foobar}. I do see how I could solve this problem somewhat generically with a list though, with a generic makelistfromargs{listname}{counter} that reads a bunch of arguments into an internal list. Is this the more common solution that you see people doing?

    – Luke
    Jun 7 '13 at 16:54













  • Perhaps it's easier for us if you provide an aim.

    – Marco Daniel
    Jun 7 '13 at 17:39











  • I can't understand the role of the counter here; what if your counter is set to 2 and you find makecsv{c}{abc}{def}{ghi}?

    – egreg
    Jun 7 '13 at 18:06











  • Is there a reason you're not considering using keys?

    – A.Ellett
    Jun 7 '13 at 18:31
















15















I have a situation where I want to define a command that takes a variable number of arguments, where the number of arguments is known programmatically via a count, and process the parameters in some way (say as if they are a list).



As an example, say I'd like to output the parameters as a comma-separated list.



newcommand{makecsv}[N]{#1, #2, ..., #N}


The code that I've come up with to do this kind of operation (in a generic-ish way) essentially takes a command, csv and expands it recursively N times. csv needs to know how to continue the recursion, and has some state that I'd like to thread through the recursion (rather than using global).



documentclass{report}
usepackage{etoolbox}

makeatletter
newcommand{ifzero}[3]{%
% #1: count
% #2: state for #3
% #3: macro to expand to
% - should take at least 2 parameters
% - ##1: count threaded through
% - ##2: macro state threaded through
ifnumc > 0
deftmp@f##1##2##3{##1{##2}{##3}}%
advance#1 -1%
else
deftmp@f##1##2##3{)}% note closeparen here (could be param)
fi
tmp@f{#3}{#1}{#2}%
}
makeatother

newcommand{csv}[3]{
% #1: count
% #2: separator state
% #3: string to concat
%
#2#3ifzero{#1}{, }{csv}%
}

newcommand{makecsv}[1]{%
ifzero{#1}{}{csv}%
}

makeatletter
newcommand{decl}[3]{%
% #1: decl id
% #2: decl symbol
% #3: # params
csgdef{decl@#1}{#2}%
globalexpandafternewcountcsname decl@#1@nparamsendcsname%
csuse{decl@#1@nparams} #3relax%
}

newcommand{usedecl}[1]{%
newcountc
c thecsuse{decl@#1@nparams}
csuse{decl@#1}(makecsv{c}%
}
makeatother

% declare some interface routines
decl{foo}{FOO}{3}
decl{bar}{BAR}{4}

begin{document}
usedecl{foo}{p1}{p2}{p3}par
usedecl{bar}{p1}{p2}{p3}{p4}par
end{document}


Is this a reasonable thing to do in 2e, or is there some sort of standard approach to this that is normally used?



Edit 1



It seems like my original MWE wasn't adequate to describe why someone might want this. I've updated the MWE with a use case. decl allows authors to declaratively define a C-style function, and usedecl allows the author to generate a use of the function, with its parameters bound to specific arguments.



This is similar enough to what I'm doing that it should help motivate the example.










share|improve this question

























  • You are asking for a list. I think you can search TeX.SX to find a lot of examples. Related packages are etoolbox or l3clist.

    – Marco Daniel
    Jun 7 '13 at 16:42











  • @Marco, I don't think that I'm asking for a list directly. I'm more interested in the technique for writing a command that can process a variable number of arguments. I can't control the source directly, so I can't change {foo}{bar}{foobar} to {foo|bar|foobar}. I do see how I could solve this problem somewhat generically with a list though, with a generic makelistfromargs{listname}{counter} that reads a bunch of arguments into an internal list. Is this the more common solution that you see people doing?

    – Luke
    Jun 7 '13 at 16:54













  • Perhaps it's easier for us if you provide an aim.

    – Marco Daniel
    Jun 7 '13 at 17:39











  • I can't understand the role of the counter here; what if your counter is set to 2 and you find makecsv{c}{abc}{def}{ghi}?

    – egreg
    Jun 7 '13 at 18:06











  • Is there a reason you're not considering using keys?

    – A.Ellett
    Jun 7 '13 at 18:31














15












15








15


5






I have a situation where I want to define a command that takes a variable number of arguments, where the number of arguments is known programmatically via a count, and process the parameters in some way (say as if they are a list).



As an example, say I'd like to output the parameters as a comma-separated list.



newcommand{makecsv}[N]{#1, #2, ..., #N}


The code that I've come up with to do this kind of operation (in a generic-ish way) essentially takes a command, csv and expands it recursively N times. csv needs to know how to continue the recursion, and has some state that I'd like to thread through the recursion (rather than using global).



documentclass{report}
usepackage{etoolbox}

makeatletter
newcommand{ifzero}[3]{%
% #1: count
% #2: state for #3
% #3: macro to expand to
% - should take at least 2 parameters
% - ##1: count threaded through
% - ##2: macro state threaded through
ifnumc > 0
deftmp@f##1##2##3{##1{##2}{##3}}%
advance#1 -1%
else
deftmp@f##1##2##3{)}% note closeparen here (could be param)
fi
tmp@f{#3}{#1}{#2}%
}
makeatother

newcommand{csv}[3]{
% #1: count
% #2: separator state
% #3: string to concat
%
#2#3ifzero{#1}{, }{csv}%
}

newcommand{makecsv}[1]{%
ifzero{#1}{}{csv}%
}

makeatletter
newcommand{decl}[3]{%
% #1: decl id
% #2: decl symbol
% #3: # params
csgdef{decl@#1}{#2}%
globalexpandafternewcountcsname decl@#1@nparamsendcsname%
csuse{decl@#1@nparams} #3relax%
}

newcommand{usedecl}[1]{%
newcountc
c thecsuse{decl@#1@nparams}
csuse{decl@#1}(makecsv{c}%
}
makeatother

% declare some interface routines
decl{foo}{FOO}{3}
decl{bar}{BAR}{4}

begin{document}
usedecl{foo}{p1}{p2}{p3}par
usedecl{bar}{p1}{p2}{p3}{p4}par
end{document}


Is this a reasonable thing to do in 2e, or is there some sort of standard approach to this that is normally used?



Edit 1



It seems like my original MWE wasn't adequate to describe why someone might want this. I've updated the MWE with a use case. decl allows authors to declaratively define a C-style function, and usedecl allows the author to generate a use of the function, with its parameters bound to specific arguments.



This is similar enough to what I'm doing that it should help motivate the example.










share|improve this question
















I have a situation where I want to define a command that takes a variable number of arguments, where the number of arguments is known programmatically via a count, and process the parameters in some way (say as if they are a list).



As an example, say I'd like to output the parameters as a comma-separated list.



newcommand{makecsv}[N]{#1, #2, ..., #N}


The code that I've come up with to do this kind of operation (in a generic-ish way) essentially takes a command, csv and expands it recursively N times. csv needs to know how to continue the recursion, and has some state that I'd like to thread through the recursion (rather than using global).



documentclass{report}
usepackage{etoolbox}

makeatletter
newcommand{ifzero}[3]{%
% #1: count
% #2: state for #3
% #3: macro to expand to
% - should take at least 2 parameters
% - ##1: count threaded through
% - ##2: macro state threaded through
ifnumc > 0
deftmp@f##1##2##3{##1{##2}{##3}}%
advance#1 -1%
else
deftmp@f##1##2##3{)}% note closeparen here (could be param)
fi
tmp@f{#3}{#1}{#2}%
}
makeatother

newcommand{csv}[3]{
% #1: count
% #2: separator state
% #3: string to concat
%
#2#3ifzero{#1}{, }{csv}%
}

newcommand{makecsv}[1]{%
ifzero{#1}{}{csv}%
}

makeatletter
newcommand{decl}[3]{%
% #1: decl id
% #2: decl symbol
% #3: # params
csgdef{decl@#1}{#2}%
globalexpandafternewcountcsname decl@#1@nparamsendcsname%
csuse{decl@#1@nparams} #3relax%
}

newcommand{usedecl}[1]{%
newcountc
c thecsuse{decl@#1@nparams}
csuse{decl@#1}(makecsv{c}%
}
makeatother

% declare some interface routines
decl{foo}{FOO}{3}
decl{bar}{BAR}{4}

begin{document}
usedecl{foo}{p1}{p2}{p3}par
usedecl{bar}{p1}{p2}{p3}{p4}par
end{document}


Is this a reasonable thing to do in 2e, or is there some sort of standard approach to this that is normally used?



Edit 1



It seems like my original MWE wasn't adequate to describe why someone might want this. I've updated the MWE with a use case. decl allows authors to declaratively define a C-style function, and usedecl allows the author to generate a use of the function, with its parameters bound to specific arguments.



This is similar enough to what I'm doing that it should help motivate the example.







macros recursion






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Jun 7 '13 at 19:07







Luke

















asked Jun 7 '13 at 16:26









LukeLuke

12717




12717













  • You are asking for a list. I think you can search TeX.SX to find a lot of examples. Related packages are etoolbox or l3clist.

    – Marco Daniel
    Jun 7 '13 at 16:42











  • @Marco, I don't think that I'm asking for a list directly. I'm more interested in the technique for writing a command that can process a variable number of arguments. I can't control the source directly, so I can't change {foo}{bar}{foobar} to {foo|bar|foobar}. I do see how I could solve this problem somewhat generically with a list though, with a generic makelistfromargs{listname}{counter} that reads a bunch of arguments into an internal list. Is this the more common solution that you see people doing?

    – Luke
    Jun 7 '13 at 16:54













  • Perhaps it's easier for us if you provide an aim.

    – Marco Daniel
    Jun 7 '13 at 17:39











  • I can't understand the role of the counter here; what if your counter is set to 2 and you find makecsv{c}{abc}{def}{ghi}?

    – egreg
    Jun 7 '13 at 18:06











  • Is there a reason you're not considering using keys?

    – A.Ellett
    Jun 7 '13 at 18:31



















  • You are asking for a list. I think you can search TeX.SX to find a lot of examples. Related packages are etoolbox or l3clist.

    – Marco Daniel
    Jun 7 '13 at 16:42











  • @Marco, I don't think that I'm asking for a list directly. I'm more interested in the technique for writing a command that can process a variable number of arguments. I can't control the source directly, so I can't change {foo}{bar}{foobar} to {foo|bar|foobar}. I do see how I could solve this problem somewhat generically with a list though, with a generic makelistfromargs{listname}{counter} that reads a bunch of arguments into an internal list. Is this the more common solution that you see people doing?

    – Luke
    Jun 7 '13 at 16:54













  • Perhaps it's easier for us if you provide an aim.

    – Marco Daniel
    Jun 7 '13 at 17:39











  • I can't understand the role of the counter here; what if your counter is set to 2 and you find makecsv{c}{abc}{def}{ghi}?

    – egreg
    Jun 7 '13 at 18:06











  • Is there a reason you're not considering using keys?

    – A.Ellett
    Jun 7 '13 at 18:31

















You are asking for a list. I think you can search TeX.SX to find a lot of examples. Related packages are etoolbox or l3clist.

– Marco Daniel
Jun 7 '13 at 16:42





You are asking for a list. I think you can search TeX.SX to find a lot of examples. Related packages are etoolbox or l3clist.

– Marco Daniel
Jun 7 '13 at 16:42













@Marco, I don't think that I'm asking for a list directly. I'm more interested in the technique for writing a command that can process a variable number of arguments. I can't control the source directly, so I can't change {foo}{bar}{foobar} to {foo|bar|foobar}. I do see how I could solve this problem somewhat generically with a list though, with a generic makelistfromargs{listname}{counter} that reads a bunch of arguments into an internal list. Is this the more common solution that you see people doing?

– Luke
Jun 7 '13 at 16:54







@Marco, I don't think that I'm asking for a list directly. I'm more interested in the technique for writing a command that can process a variable number of arguments. I can't control the source directly, so I can't change {foo}{bar}{foobar} to {foo|bar|foobar}. I do see how I could solve this problem somewhat generically with a list though, with a generic makelistfromargs{listname}{counter} that reads a bunch of arguments into an internal list. Is this the more common solution that you see people doing?

– Luke
Jun 7 '13 at 16:54















Perhaps it's easier for us if you provide an aim.

– Marco Daniel
Jun 7 '13 at 17:39





Perhaps it's easier for us if you provide an aim.

– Marco Daniel
Jun 7 '13 at 17:39













I can't understand the role of the counter here; what if your counter is set to 2 and you find makecsv{c}{abc}{def}{ghi}?

– egreg
Jun 7 '13 at 18:06





I can't understand the role of the counter here; what if your counter is set to 2 and you find makecsv{c}{abc}{def}{ghi}?

– egreg
Jun 7 '13 at 18:06













Is there a reason you're not considering using keys?

– A.Ellett
Jun 7 '13 at 18:31





Is there a reason you're not considering using keys?

– A.Ellett
Jun 7 '13 at 18:31










4 Answers
4






active

oldest

votes


















8














As commented, here a solution that uses @ifnextchar. I also implemented checks against too many or too few arguments (or why are they provided by the user?).



The @ifnextchar(or its “very internal” big brother kernel@ifnextchar) skips spaces which results in removed spaces in the third and fourth example.



Code



documentclass{report}
usepackage{etoolbox}
makeatletter
newcommand*{decl}[3]{%
% #1: decl id
% #2: decl symbol
% #3: # params
csdef{decl@symbol@#1}{#2}%
expandafternewcountcsname c@decl@params@#1endcsname
csuse{c@decl@params@#1}=#3relax
}
newcountdecl@params@check
newcommand*{usedecl}[1]{%
defdecl@name{#1}%
edefdecl@params{thecsuse{c@decl@params@#1}}%
defdecl@symbol{csuse{decl@symbol@#1}}%
decl@params@check=z@
letdecl@list@gobble % the @gobble removes the first , (expandable)
defdecl@next{kernel@ifnextcharbgroupuse@decluse@decl@finish}%
decl@next
}
newcommand*{use@decl}[1]{%
advancedecl@params@check@ne
expandafterifnumthedecl@params@check>decl@paramsrelax % too many!
PackageWarning{decl}{You have used more params than the decl@namespace function expected!
I ignore this (and any following) param, ok?}% but insert the extra argument anyway?!
defdecl@next{use@decl@finish{#1}}% the extra pair of braces {} keeps '#1' local as it is in the input stream
else
expandafterdefexpandafterdecl@listexpandafter{decl@listdecl@list@sep#1}%
fi
decl@next
}
newififuse@decl@message
newcommand*{use@decl@finish}{%
ifnumdecl@params@check<decl@paramsrelax % too few!
expandafter@firstoftwo
else
expandafter@secondoftwo
fi
{%
ifuse@decl@messageelse
PackageWarning{decl}{You have used fewer params than the decl@namespace function expected! I'm filling up with '??'!}%
use@decl@messagetrue
fi
use@decl{??}}
{%
decl@symboldecl@list@startdecl@listdecl@list@end
use@decl@messagefalse
}%
}
newcommand*{setdeclstart}[1]{defdecl@list@start{#1}}
newcommand*{setdeclend}[1]{defdecl@list@end{#1}}
newcommand*{setdeclsep}[1]{defdecl@list@sep{#1}}
makeatother

setdeclstart{(}
setdeclend{)}
setdeclsep{, }

% declare some interface routines
decl{foo}{FOO}{3}
decl{bar}{BAR}{4}

begin{document}
given $P$, $Q$ and $R$ such dots that $usedecl{foo}{P}{Q}{R}$ results in dotspar
given P, Q and R such dots that usedecl{foo}{P}{Q}{R} results in dotspar
usedecl{foo}{p1}{p2}{p3}par
usedecl{bar}{p1}{p2}{p3}{p4}par
usedecl{bar}{p1} foopar
usedecl{foo}{p1}{p2}{p3} {p4}par
end{document}


Output



enter image description here






share|improve this answer

































    9














    If you're willing to peek ahead, you can check whether there's "another argument" and keep gobbling them on the fly:



    enter image description here



    documentclass{article}
    usepackage{etoolbox}% http://ctan.org/pkg/etoolbox

    makeatletter
    newcommand{newdecl}[2]{csgdef{decl@#1}{#2}}% Creates a declaration
    newcommand{csvdel}{}% Delimiter used in CSV representation
    newcommand{newusedecl}[2][,]{% Use a declaration
    renewcommand{csvdel}{renewcommand{csvdel}{#1,}}% Delay csvdel one cycle.
    csname decl@#2endcsname(checknextarg}
    newcommand{checknextarg}{@ifnextcharbgroup{gobblenext}{}}% Check if another "argument" exists
    newcommand{gobblenext}[1]{csvdel#1@ifnextcharbgroup{gobblenext}{)}}% Gobble next "argument"
    makeatother

    % declare some interface routines
    newdecl{foo}{FOO}
    newdecl{bar}{BAR}

    begin{document}

    newusedecl{foo}{p1}{p2}{p3}par
    newusedecl{bar}{p1}{p2}{p3}{p4}{p1}{p2}{p3}{p4}{p1}{p2}{p3}{p4}{p1}{p2}{p3}{p4}
    {p1}{p2}{p3}{p4}par
    newusedecl[;]{foo}{p1}{p2}{p3}{p4}{p1}{p2}{p3}{p4}{p1}{p2}{p3}{p4}{p1}{p2}{p3}{p4}
    {p1}{p2}{p3}{p4}

    end{document}


    The peeking is done using @ifnextchar. For some explanation around this, see Understanding @ifnextchar. The delayed use of csvdel (the CSV delimiter) stems from Cunning (La)TeX tricks).



    The optional argument to newusedecl adapts csvdel.






    share|improve this answer


























    • Ah, that's nice. So the #1 in gobblenext is the next parameter contents, which I can process as necessary. I think that this is a nice alternative to the way that I'm doing it, but I'm not sure that it's more maintainable than what I've got. I'm going to accept it as an answer and just conclude that this isn't done often enough for there to be a "commonly accepted" pattern.

      – Luke
      Jun 7 '13 at 19:42











    • @Luke: You can adapt the solution to your liking. I'm not entirely sure of your use-case. I've added an optional argument to newusedecl that allows you to update/specify the CSV delimiter. Default is ,.

      – Werner
      Jun 7 '13 at 20:02











    • There is an understandable exampke usage of this idea: davidyat.es/2016/07/27/…. I understood Werner's comments much better after reading that.

      – pauljohn32
      Mar 29 '18 at 9:15



















    3














    You've not given us much to go off of, but here's something that seems to implement what you want while being fed a comma separated list (in lieu of passing a variable number of arguments).



    documentclass{article}
    usepackage{xparse}
    newcounter{myargcounter}
    ExplSyntaxOn
    clist_new:N l_myvararg_parameters_clist
    tl_new:N l_myvararg_current_item_tl
    NewDocumentCommand{makecsv}{ m }
    {
    clist_set:Nn l_myvararg_parameters_clist { #1 }
    int_while_do:nNnn { clist_count:N l_myvararg_parameters_clist } > { 1 }
    {
    clist_pop:NN l_myvararg_parameters_clist l_myvararg_current_item_tl
    tl_use:N l_myvararg_current_item_tl,
    }
    clist_pop:NN l_myvararg_parameters_clist
    l_myvararg_current_item_tl
    {} ~ and ~ tl_use:N l_myvararg_current_item_tl
    }
    ExplSyntaxOff
    pagestyle{empty}
    begin{document}
    makecsv{a,b,c,d}

    makecsv{a,b,c,d,e,f,g}

    makecsv{a,b}

    end{document}





    share|improve this answer
























    • Thanks, but as previously stated, I don't have the flexibility to adjust the use-site of makecsv, nor do I want to rely on any non-2e stuff.

      – Luke
      Jun 7 '13 at 19:09











    • I'm not sure I understand what you mean by use-site.

      – A.Ellett
      Jun 7 '13 at 19:26











    • I mean as opposed to definition site. That is, I control the .sty where makecsv it is defined, but I am stuck with the way that .tex authors are already using it. I can't get them to change from makecsv{c}{p1}{p2}{p3} to makecsv{p1, p2, p3} without a meeting that I don't want to have, since I can actually provide what they want without the API change.

      – Luke
      Jun 7 '13 at 19:33











    • This is a valuable answer! Even though OP cannot use it, readers should not overlook. Similar solution tex.stackexchange.com/questions/417064/…. Only caution: require newer LaTeX distribution

      – pauljohn32
      Mar 29 '18 at 9:22





















    0














    Here is an expansion-based implementation where the loop for gathering the parameters is based on romannumeral both for triggering expansion and for keeping track of the amount of parameters that still is to be collected.



    The usedecl-mechanism does without using any count-registers and without whatsoever temporary assignments for carrying out the loop. e-TeX-extensions and whatsoever fancy packages are not needed. ;-)



    Due to romannumeral-expansion usedecl delivers the result after two expansion-steps/after being hit by expandafter twice.



    documentclass{report}

    makeatletter
    %%----------------------------------------------------------------------
    %% Form control sequence token from sequence of characters denoting its
    %% name:
    %% UD@name foo{bar} -> foobar
    %% , e.g.,
    %% UD@namenewcommand*{foo} -> newcommand*foo
    %% UD@namestring{foo} -> stringfoo
    %% UD@nameUD@namelet{foo}={bar} -> UD@nameletfoo={bar} -> letfoo=bar
    %% UD@name{foo} -> foo
    %%......................................................................
    @ifdefinableUD@name{%
    longdefUD@name#1#{romannumeral0UD@innername{#1}}%
    }%
    newcommandUD@innername[2]{%
    expandafterUD@exchangeexpandafter{csname#2endcsname}{ #1}%
    }%
    newcommandUD@exchange[2]{#2#1}%
    %%----------------------------------------------------------------------
    %% Check whether argument is empty:
    %%......................................................................
    %% UD@CheckWhetherNull{<Argument which is to be checked>}%
    %% {<Tokens to be delivered in case that argument
    %% which is to be checked is empty>}%
    %% {<Tokens to be delivered in case that argument
    %% which is to be checked is not empty>}%
    %% The gist of this macro comes from Robert R. Schneck's ifempty-macro:
    %% <https://groups.google.com/forum/#!original/comp.text.tex/kuOEIQIrElc/lUg37FmhA74J>
    newcommandUD@CheckWhetherNull[1]{%
    romannumeral0expandafter@secondoftwostring{expandafter
    @secondoftwoexpandafter{expandafter{string#1}expandafter
    @secondoftwostring}expandafter@firstoftwoexpandafter{expandafter
    @secondoftwostring}expandafterexpandafter@firstoftwo{ }{}%
    @secondoftwo}{expandafterexpandafter@firstoftwo{ }{}@firstoftwo}%
    }%
    %%----------------------------------------------------------------------
    %% Associate IDs with amounts of parameters and symbols:
    %% No count-registers get allocated/get wasted. Just macros get defined.
    %%......................................................................
    newcommanddecl[3]{%
    % #1: decl id; #2: decl symbol; #3: # params
    UD@namenewcommand*{decl@#1}{#2}%
    UD@namenewcommand*{decl@#1@nparams}{#3}%
    }%
    %%----------------------------------------------------------------------
    %% usedecl{<decl id>}<list of parameters of length decl@<decl id>@nparams>
    %%
    %% creates comma list from parameters, nested in parentheses and
    %% lead by the tokens that come from expanding decl@<decl id>
    %%
    %% Due to romannumeral-expansion the result is delivered after two
    %% expansion-steps/after "hitting" usedecl by expandafter twice.
    %%......................................................................
    newcommandusedecl[1]{%
    romannumeral0%
    expandafterUD@exchange
    expandafter{%
    expandafter{%
    romannumeral0%
    UD@exchange{ }{UD@nameexpandafter}{decl@#1}%
    }{}{}%
    }{%
    expandafterusedeclloop
    expandafter{%
    romannumeralUD@namenumbernumber{decl@#1@nparams} 000 %
    }%
    }%
    }%
    newcommandusedeclloop[4]{%
    %#1 amount of m = amount of parameters to collect
    %#2 decl symbol
    %#3 prepend-tokens
    %#4 parameters collected so far
    UD@CheckWhetherNull{#1}{ #2(#4)}{%
    expandafterusedeclloopatfetchexpandafter{@gobble#1}{#2}{, }{#4#3}%
    }%
    }%
    newcommandusedeclloopatfetch[5]{%
    %#1 amount of m = amount of parameters to collect
    %#2 decl symbol
    %#3 prepend tokens
    %#4 parameters collected so far
    %#5 next parameter fetched.
    usedeclloop{#1}{#2}{#3}{#4#5}%
    }%
    newcommandwithoutdecl[2]{%
    romannumeral0%
    expandafterusedeclloopexpandafter{romannumeralnumbernumber#2 000 }{#1}%
    {}{}%
    }%

    makeatother

    % declare the symbols and the amounts of parameters:
    decl{foo}{FOO}{3}
    decl{bar}{BAR}{4}

    begin{document}

    usedecl{foo}{p1}{p2}{p3}par
    usedecl{bar}{p1}{p2}{p3}{p4}par
    withoutdecl{BAZ}{5}{p1}{p2}{p3}{p4}{p5}par

    expandafterexpandafterexpandafterdef
    expandafterexpandafterexpandafterfoolist
    expandafterexpandafterexpandafter{%
    usedecl{foo}{p1}{p2}{p3}%
    }%

    expandafterexpandafterexpandafterdef
    expandafterexpandafterexpandafterbarlist
    expandafterexpandafterexpandafter{%
    usedecl{bar}{p1}{p2}{p3}{p4}%
    }%

    expandafterexpandafterexpandafterdef
    expandafterexpandafterexpandafterbazlist
    expandafterexpandafterexpandafter{%
    withoutdecl{BAZ}{5}{p1}{p2}{p3}{p4}{p5}%
    }%

    texttt{|frenchspacingstringfoolist=meaningfoolist|}

    texttt{|frenchspacingstringbarlist=meaningbarlist|}

    texttt{|frenchspacingstringbazlist=meaningbazlist|}

    end{document}


    enter image description here





    share























      Your Answer








      StackExchange.ready(function() {
      var channelOptions = {
      tags: "".split(" "),
      id: "85"
      };
      initTagRenderer("".split(" "), "".split(" "), channelOptions);

      StackExchange.using("externalEditor", function() {
      // Have to fire editor after snippets, if snippets enabled
      if (StackExchange.settings.snippets.snippetsEnabled) {
      StackExchange.using("snippets", function() {
      createEditor();
      });
      }
      else {
      createEditor();
      }
      });

      function createEditor() {
      StackExchange.prepareEditor({
      heartbeatType: 'answer',
      autoActivateHeartbeat: false,
      convertImagesToLinks: false,
      noModals: true,
      showLowRepImageUploadWarning: true,
      reputationToPostImages: null,
      bindNavPrevention: true,
      postfix: "",
      imageUploader: {
      brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
      contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
      allowUrls: true
      },
      onDemand: true,
      discardSelector: ".discard-answer"
      ,immediatelyShowMarkdownHelp:true
      });


      }
      });














      draft saved

      draft discarded


















      StackExchange.ready(
      function () {
      StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2ftex.stackexchange.com%2fquestions%2f118114%2fcommands-that-may-take-a-variable-number-of-arguments%23new-answer', 'question_page');
      }
      );

      Post as a guest















      Required, but never shown

























      4 Answers
      4






      active

      oldest

      votes








      4 Answers
      4






      active

      oldest

      votes









      active

      oldest

      votes






      active

      oldest

      votes









      8














      As commented, here a solution that uses @ifnextchar. I also implemented checks against too many or too few arguments (or why are they provided by the user?).



      The @ifnextchar(or its “very internal” big brother kernel@ifnextchar) skips spaces which results in removed spaces in the third and fourth example.



      Code



      documentclass{report}
      usepackage{etoolbox}
      makeatletter
      newcommand*{decl}[3]{%
      % #1: decl id
      % #2: decl symbol
      % #3: # params
      csdef{decl@symbol@#1}{#2}%
      expandafternewcountcsname c@decl@params@#1endcsname
      csuse{c@decl@params@#1}=#3relax
      }
      newcountdecl@params@check
      newcommand*{usedecl}[1]{%
      defdecl@name{#1}%
      edefdecl@params{thecsuse{c@decl@params@#1}}%
      defdecl@symbol{csuse{decl@symbol@#1}}%
      decl@params@check=z@
      letdecl@list@gobble % the @gobble removes the first , (expandable)
      defdecl@next{kernel@ifnextcharbgroupuse@decluse@decl@finish}%
      decl@next
      }
      newcommand*{use@decl}[1]{%
      advancedecl@params@check@ne
      expandafterifnumthedecl@params@check>decl@paramsrelax % too many!
      PackageWarning{decl}{You have used more params than the decl@namespace function expected!
      I ignore this (and any following) param, ok?}% but insert the extra argument anyway?!
      defdecl@next{use@decl@finish{#1}}% the extra pair of braces {} keeps '#1' local as it is in the input stream
      else
      expandafterdefexpandafterdecl@listexpandafter{decl@listdecl@list@sep#1}%
      fi
      decl@next
      }
      newififuse@decl@message
      newcommand*{use@decl@finish}{%
      ifnumdecl@params@check<decl@paramsrelax % too few!
      expandafter@firstoftwo
      else
      expandafter@secondoftwo
      fi
      {%
      ifuse@decl@messageelse
      PackageWarning{decl}{You have used fewer params than the decl@namespace function expected! I'm filling up with '??'!}%
      use@decl@messagetrue
      fi
      use@decl{??}}
      {%
      decl@symboldecl@list@startdecl@listdecl@list@end
      use@decl@messagefalse
      }%
      }
      newcommand*{setdeclstart}[1]{defdecl@list@start{#1}}
      newcommand*{setdeclend}[1]{defdecl@list@end{#1}}
      newcommand*{setdeclsep}[1]{defdecl@list@sep{#1}}
      makeatother

      setdeclstart{(}
      setdeclend{)}
      setdeclsep{, }

      % declare some interface routines
      decl{foo}{FOO}{3}
      decl{bar}{BAR}{4}

      begin{document}
      given $P$, $Q$ and $R$ such dots that $usedecl{foo}{P}{Q}{R}$ results in dotspar
      given P, Q and R such dots that usedecl{foo}{P}{Q}{R} results in dotspar
      usedecl{foo}{p1}{p2}{p3}par
      usedecl{bar}{p1}{p2}{p3}{p4}par
      usedecl{bar}{p1} foopar
      usedecl{foo}{p1}{p2}{p3} {p4}par
      end{document}


      Output



      enter image description here






      share|improve this answer






























        8














        As commented, here a solution that uses @ifnextchar. I also implemented checks against too many or too few arguments (or why are they provided by the user?).



        The @ifnextchar(or its “very internal” big brother kernel@ifnextchar) skips spaces which results in removed spaces in the third and fourth example.



        Code



        documentclass{report}
        usepackage{etoolbox}
        makeatletter
        newcommand*{decl}[3]{%
        % #1: decl id
        % #2: decl symbol
        % #3: # params
        csdef{decl@symbol@#1}{#2}%
        expandafternewcountcsname c@decl@params@#1endcsname
        csuse{c@decl@params@#1}=#3relax
        }
        newcountdecl@params@check
        newcommand*{usedecl}[1]{%
        defdecl@name{#1}%
        edefdecl@params{thecsuse{c@decl@params@#1}}%
        defdecl@symbol{csuse{decl@symbol@#1}}%
        decl@params@check=z@
        letdecl@list@gobble % the @gobble removes the first , (expandable)
        defdecl@next{kernel@ifnextcharbgroupuse@decluse@decl@finish}%
        decl@next
        }
        newcommand*{use@decl}[1]{%
        advancedecl@params@check@ne
        expandafterifnumthedecl@params@check>decl@paramsrelax % too many!
        PackageWarning{decl}{You have used more params than the decl@namespace function expected!
        I ignore this (and any following) param, ok?}% but insert the extra argument anyway?!
        defdecl@next{use@decl@finish{#1}}% the extra pair of braces {} keeps '#1' local as it is in the input stream
        else
        expandafterdefexpandafterdecl@listexpandafter{decl@listdecl@list@sep#1}%
        fi
        decl@next
        }
        newififuse@decl@message
        newcommand*{use@decl@finish}{%
        ifnumdecl@params@check<decl@paramsrelax % too few!
        expandafter@firstoftwo
        else
        expandafter@secondoftwo
        fi
        {%
        ifuse@decl@messageelse
        PackageWarning{decl}{You have used fewer params than the decl@namespace function expected! I'm filling up with '??'!}%
        use@decl@messagetrue
        fi
        use@decl{??}}
        {%
        decl@symboldecl@list@startdecl@listdecl@list@end
        use@decl@messagefalse
        }%
        }
        newcommand*{setdeclstart}[1]{defdecl@list@start{#1}}
        newcommand*{setdeclend}[1]{defdecl@list@end{#1}}
        newcommand*{setdeclsep}[1]{defdecl@list@sep{#1}}
        makeatother

        setdeclstart{(}
        setdeclend{)}
        setdeclsep{, }

        % declare some interface routines
        decl{foo}{FOO}{3}
        decl{bar}{BAR}{4}

        begin{document}
        given $P$, $Q$ and $R$ such dots that $usedecl{foo}{P}{Q}{R}$ results in dotspar
        given P, Q and R such dots that usedecl{foo}{P}{Q}{R} results in dotspar
        usedecl{foo}{p1}{p2}{p3}par
        usedecl{bar}{p1}{p2}{p3}{p4}par
        usedecl{bar}{p1} foopar
        usedecl{foo}{p1}{p2}{p3} {p4}par
        end{document}


        Output



        enter image description here






        share|improve this answer




























          8












          8








          8







          As commented, here a solution that uses @ifnextchar. I also implemented checks against too many or too few arguments (or why are they provided by the user?).



          The @ifnextchar(or its “very internal” big brother kernel@ifnextchar) skips spaces which results in removed spaces in the third and fourth example.



          Code



          documentclass{report}
          usepackage{etoolbox}
          makeatletter
          newcommand*{decl}[3]{%
          % #1: decl id
          % #2: decl symbol
          % #3: # params
          csdef{decl@symbol@#1}{#2}%
          expandafternewcountcsname c@decl@params@#1endcsname
          csuse{c@decl@params@#1}=#3relax
          }
          newcountdecl@params@check
          newcommand*{usedecl}[1]{%
          defdecl@name{#1}%
          edefdecl@params{thecsuse{c@decl@params@#1}}%
          defdecl@symbol{csuse{decl@symbol@#1}}%
          decl@params@check=z@
          letdecl@list@gobble % the @gobble removes the first , (expandable)
          defdecl@next{kernel@ifnextcharbgroupuse@decluse@decl@finish}%
          decl@next
          }
          newcommand*{use@decl}[1]{%
          advancedecl@params@check@ne
          expandafterifnumthedecl@params@check>decl@paramsrelax % too many!
          PackageWarning{decl}{You have used more params than the decl@namespace function expected!
          I ignore this (and any following) param, ok?}% but insert the extra argument anyway?!
          defdecl@next{use@decl@finish{#1}}% the extra pair of braces {} keeps '#1' local as it is in the input stream
          else
          expandafterdefexpandafterdecl@listexpandafter{decl@listdecl@list@sep#1}%
          fi
          decl@next
          }
          newififuse@decl@message
          newcommand*{use@decl@finish}{%
          ifnumdecl@params@check<decl@paramsrelax % too few!
          expandafter@firstoftwo
          else
          expandafter@secondoftwo
          fi
          {%
          ifuse@decl@messageelse
          PackageWarning{decl}{You have used fewer params than the decl@namespace function expected! I'm filling up with '??'!}%
          use@decl@messagetrue
          fi
          use@decl{??}}
          {%
          decl@symboldecl@list@startdecl@listdecl@list@end
          use@decl@messagefalse
          }%
          }
          newcommand*{setdeclstart}[1]{defdecl@list@start{#1}}
          newcommand*{setdeclend}[1]{defdecl@list@end{#1}}
          newcommand*{setdeclsep}[1]{defdecl@list@sep{#1}}
          makeatother

          setdeclstart{(}
          setdeclend{)}
          setdeclsep{, }

          % declare some interface routines
          decl{foo}{FOO}{3}
          decl{bar}{BAR}{4}

          begin{document}
          given $P$, $Q$ and $R$ such dots that $usedecl{foo}{P}{Q}{R}$ results in dotspar
          given P, Q and R such dots that usedecl{foo}{P}{Q}{R} results in dotspar
          usedecl{foo}{p1}{p2}{p3}par
          usedecl{bar}{p1}{p2}{p3}{p4}par
          usedecl{bar}{p1} foopar
          usedecl{foo}{p1}{p2}{p3} {p4}par
          end{document}


          Output



          enter image description here






          share|improve this answer















          As commented, here a solution that uses @ifnextchar. I also implemented checks against too many or too few arguments (or why are they provided by the user?).



          The @ifnextchar(or its “very internal” big brother kernel@ifnextchar) skips spaces which results in removed spaces in the third and fourth example.



          Code



          documentclass{report}
          usepackage{etoolbox}
          makeatletter
          newcommand*{decl}[3]{%
          % #1: decl id
          % #2: decl symbol
          % #3: # params
          csdef{decl@symbol@#1}{#2}%
          expandafternewcountcsname c@decl@params@#1endcsname
          csuse{c@decl@params@#1}=#3relax
          }
          newcountdecl@params@check
          newcommand*{usedecl}[1]{%
          defdecl@name{#1}%
          edefdecl@params{thecsuse{c@decl@params@#1}}%
          defdecl@symbol{csuse{decl@symbol@#1}}%
          decl@params@check=z@
          letdecl@list@gobble % the @gobble removes the first , (expandable)
          defdecl@next{kernel@ifnextcharbgroupuse@decluse@decl@finish}%
          decl@next
          }
          newcommand*{use@decl}[1]{%
          advancedecl@params@check@ne
          expandafterifnumthedecl@params@check>decl@paramsrelax % too many!
          PackageWarning{decl}{You have used more params than the decl@namespace function expected!
          I ignore this (and any following) param, ok?}% but insert the extra argument anyway?!
          defdecl@next{use@decl@finish{#1}}% the extra pair of braces {} keeps '#1' local as it is in the input stream
          else
          expandafterdefexpandafterdecl@listexpandafter{decl@listdecl@list@sep#1}%
          fi
          decl@next
          }
          newififuse@decl@message
          newcommand*{use@decl@finish}{%
          ifnumdecl@params@check<decl@paramsrelax % too few!
          expandafter@firstoftwo
          else
          expandafter@secondoftwo
          fi
          {%
          ifuse@decl@messageelse
          PackageWarning{decl}{You have used fewer params than the decl@namespace function expected! I'm filling up with '??'!}%
          use@decl@messagetrue
          fi
          use@decl{??}}
          {%
          decl@symboldecl@list@startdecl@listdecl@list@end
          use@decl@messagefalse
          }%
          }
          newcommand*{setdeclstart}[1]{defdecl@list@start{#1}}
          newcommand*{setdeclend}[1]{defdecl@list@end{#1}}
          newcommand*{setdeclsep}[1]{defdecl@list@sep{#1}}
          makeatother

          setdeclstart{(}
          setdeclend{)}
          setdeclsep{, }

          % declare some interface routines
          decl{foo}{FOO}{3}
          decl{bar}{BAR}{4}

          begin{document}
          given $P$, $Q$ and $R$ such dots that $usedecl{foo}{P}{Q}{R}$ results in dotspar
          given P, Q and R such dots that usedecl{foo}{P}{Q}{R} results in dotspar
          usedecl{foo}{p1}{p2}{p3}par
          usedecl{bar}{p1}{p2}{p3}{p4}par
          usedecl{bar}{p1} foopar
          usedecl{foo}{p1}{p2}{p3} {p4}par
          end{document}


          Output



          enter image description here







          share|improve this answer














          share|improve this answer



          share|improve this answer








          edited Jun 8 '13 at 1:13

























          answered Jun 7 '13 at 20:02









          QrrbrbirlbelQrrbrbirlbel

          77.9k4184319




          77.9k4184319























              9














              If you're willing to peek ahead, you can check whether there's "another argument" and keep gobbling them on the fly:



              enter image description here



              documentclass{article}
              usepackage{etoolbox}% http://ctan.org/pkg/etoolbox

              makeatletter
              newcommand{newdecl}[2]{csgdef{decl@#1}{#2}}% Creates a declaration
              newcommand{csvdel}{}% Delimiter used in CSV representation
              newcommand{newusedecl}[2][,]{% Use a declaration
              renewcommand{csvdel}{renewcommand{csvdel}{#1,}}% Delay csvdel one cycle.
              csname decl@#2endcsname(checknextarg}
              newcommand{checknextarg}{@ifnextcharbgroup{gobblenext}{}}% Check if another "argument" exists
              newcommand{gobblenext}[1]{csvdel#1@ifnextcharbgroup{gobblenext}{)}}% Gobble next "argument"
              makeatother

              % declare some interface routines
              newdecl{foo}{FOO}
              newdecl{bar}{BAR}

              begin{document}

              newusedecl{foo}{p1}{p2}{p3}par
              newusedecl{bar}{p1}{p2}{p3}{p4}{p1}{p2}{p3}{p4}{p1}{p2}{p3}{p4}{p1}{p2}{p3}{p4}
              {p1}{p2}{p3}{p4}par
              newusedecl[;]{foo}{p1}{p2}{p3}{p4}{p1}{p2}{p3}{p4}{p1}{p2}{p3}{p4}{p1}{p2}{p3}{p4}
              {p1}{p2}{p3}{p4}

              end{document}


              The peeking is done using @ifnextchar. For some explanation around this, see Understanding @ifnextchar. The delayed use of csvdel (the CSV delimiter) stems from Cunning (La)TeX tricks).



              The optional argument to newusedecl adapts csvdel.






              share|improve this answer


























              • Ah, that's nice. So the #1 in gobblenext is the next parameter contents, which I can process as necessary. I think that this is a nice alternative to the way that I'm doing it, but I'm not sure that it's more maintainable than what I've got. I'm going to accept it as an answer and just conclude that this isn't done often enough for there to be a "commonly accepted" pattern.

                – Luke
                Jun 7 '13 at 19:42











              • @Luke: You can adapt the solution to your liking. I'm not entirely sure of your use-case. I've added an optional argument to newusedecl that allows you to update/specify the CSV delimiter. Default is ,.

                – Werner
                Jun 7 '13 at 20:02











              • There is an understandable exampke usage of this idea: davidyat.es/2016/07/27/…. I understood Werner's comments much better after reading that.

                – pauljohn32
                Mar 29 '18 at 9:15
















              9














              If you're willing to peek ahead, you can check whether there's "another argument" and keep gobbling them on the fly:



              enter image description here



              documentclass{article}
              usepackage{etoolbox}% http://ctan.org/pkg/etoolbox

              makeatletter
              newcommand{newdecl}[2]{csgdef{decl@#1}{#2}}% Creates a declaration
              newcommand{csvdel}{}% Delimiter used in CSV representation
              newcommand{newusedecl}[2][,]{% Use a declaration
              renewcommand{csvdel}{renewcommand{csvdel}{#1,}}% Delay csvdel one cycle.
              csname decl@#2endcsname(checknextarg}
              newcommand{checknextarg}{@ifnextcharbgroup{gobblenext}{}}% Check if another "argument" exists
              newcommand{gobblenext}[1]{csvdel#1@ifnextcharbgroup{gobblenext}{)}}% Gobble next "argument"
              makeatother

              % declare some interface routines
              newdecl{foo}{FOO}
              newdecl{bar}{BAR}

              begin{document}

              newusedecl{foo}{p1}{p2}{p3}par
              newusedecl{bar}{p1}{p2}{p3}{p4}{p1}{p2}{p3}{p4}{p1}{p2}{p3}{p4}{p1}{p2}{p3}{p4}
              {p1}{p2}{p3}{p4}par
              newusedecl[;]{foo}{p1}{p2}{p3}{p4}{p1}{p2}{p3}{p4}{p1}{p2}{p3}{p4}{p1}{p2}{p3}{p4}
              {p1}{p2}{p3}{p4}

              end{document}


              The peeking is done using @ifnextchar. For some explanation around this, see Understanding @ifnextchar. The delayed use of csvdel (the CSV delimiter) stems from Cunning (La)TeX tricks).



              The optional argument to newusedecl adapts csvdel.






              share|improve this answer


























              • Ah, that's nice. So the #1 in gobblenext is the next parameter contents, which I can process as necessary. I think that this is a nice alternative to the way that I'm doing it, but I'm not sure that it's more maintainable than what I've got. I'm going to accept it as an answer and just conclude that this isn't done often enough for there to be a "commonly accepted" pattern.

                – Luke
                Jun 7 '13 at 19:42











              • @Luke: You can adapt the solution to your liking. I'm not entirely sure of your use-case. I've added an optional argument to newusedecl that allows you to update/specify the CSV delimiter. Default is ,.

                – Werner
                Jun 7 '13 at 20:02











              • There is an understandable exampke usage of this idea: davidyat.es/2016/07/27/…. I understood Werner's comments much better after reading that.

                – pauljohn32
                Mar 29 '18 at 9:15














              9












              9








              9







              If you're willing to peek ahead, you can check whether there's "another argument" and keep gobbling them on the fly:



              enter image description here



              documentclass{article}
              usepackage{etoolbox}% http://ctan.org/pkg/etoolbox

              makeatletter
              newcommand{newdecl}[2]{csgdef{decl@#1}{#2}}% Creates a declaration
              newcommand{csvdel}{}% Delimiter used in CSV representation
              newcommand{newusedecl}[2][,]{% Use a declaration
              renewcommand{csvdel}{renewcommand{csvdel}{#1,}}% Delay csvdel one cycle.
              csname decl@#2endcsname(checknextarg}
              newcommand{checknextarg}{@ifnextcharbgroup{gobblenext}{}}% Check if another "argument" exists
              newcommand{gobblenext}[1]{csvdel#1@ifnextcharbgroup{gobblenext}{)}}% Gobble next "argument"
              makeatother

              % declare some interface routines
              newdecl{foo}{FOO}
              newdecl{bar}{BAR}

              begin{document}

              newusedecl{foo}{p1}{p2}{p3}par
              newusedecl{bar}{p1}{p2}{p3}{p4}{p1}{p2}{p3}{p4}{p1}{p2}{p3}{p4}{p1}{p2}{p3}{p4}
              {p1}{p2}{p3}{p4}par
              newusedecl[;]{foo}{p1}{p2}{p3}{p4}{p1}{p2}{p3}{p4}{p1}{p2}{p3}{p4}{p1}{p2}{p3}{p4}
              {p1}{p2}{p3}{p4}

              end{document}


              The peeking is done using @ifnextchar. For some explanation around this, see Understanding @ifnextchar. The delayed use of csvdel (the CSV delimiter) stems from Cunning (La)TeX tricks).



              The optional argument to newusedecl adapts csvdel.






              share|improve this answer















              If you're willing to peek ahead, you can check whether there's "another argument" and keep gobbling them on the fly:



              enter image description here



              documentclass{article}
              usepackage{etoolbox}% http://ctan.org/pkg/etoolbox

              makeatletter
              newcommand{newdecl}[2]{csgdef{decl@#1}{#2}}% Creates a declaration
              newcommand{csvdel}{}% Delimiter used in CSV representation
              newcommand{newusedecl}[2][,]{% Use a declaration
              renewcommand{csvdel}{renewcommand{csvdel}{#1,}}% Delay csvdel one cycle.
              csname decl@#2endcsname(checknextarg}
              newcommand{checknextarg}{@ifnextcharbgroup{gobblenext}{}}% Check if another "argument" exists
              newcommand{gobblenext}[1]{csvdel#1@ifnextcharbgroup{gobblenext}{)}}% Gobble next "argument"
              makeatother

              % declare some interface routines
              newdecl{foo}{FOO}
              newdecl{bar}{BAR}

              begin{document}

              newusedecl{foo}{p1}{p2}{p3}par
              newusedecl{bar}{p1}{p2}{p3}{p4}{p1}{p2}{p3}{p4}{p1}{p2}{p3}{p4}{p1}{p2}{p3}{p4}
              {p1}{p2}{p3}{p4}par
              newusedecl[;]{foo}{p1}{p2}{p3}{p4}{p1}{p2}{p3}{p4}{p1}{p2}{p3}{p4}{p1}{p2}{p3}{p4}
              {p1}{p2}{p3}{p4}

              end{document}


              The peeking is done using @ifnextchar. For some explanation around this, see Understanding @ifnextchar. The delayed use of csvdel (the CSV delimiter) stems from Cunning (La)TeX tricks).



              The optional argument to newusedecl adapts csvdel.







              share|improve this answer














              share|improve this answer



              share|improve this answer








              edited Apr 13 '17 at 12:35









              Community

              1




              1










              answered Jun 7 '13 at 19:29









              WernerWerner

              449k719941699




              449k719941699













              • Ah, that's nice. So the #1 in gobblenext is the next parameter contents, which I can process as necessary. I think that this is a nice alternative to the way that I'm doing it, but I'm not sure that it's more maintainable than what I've got. I'm going to accept it as an answer and just conclude that this isn't done often enough for there to be a "commonly accepted" pattern.

                – Luke
                Jun 7 '13 at 19:42











              • @Luke: You can adapt the solution to your liking. I'm not entirely sure of your use-case. I've added an optional argument to newusedecl that allows you to update/specify the CSV delimiter. Default is ,.

                – Werner
                Jun 7 '13 at 20:02











              • There is an understandable exampke usage of this idea: davidyat.es/2016/07/27/…. I understood Werner's comments much better after reading that.

                – pauljohn32
                Mar 29 '18 at 9:15



















              • Ah, that's nice. So the #1 in gobblenext is the next parameter contents, which I can process as necessary. I think that this is a nice alternative to the way that I'm doing it, but I'm not sure that it's more maintainable than what I've got. I'm going to accept it as an answer and just conclude that this isn't done often enough for there to be a "commonly accepted" pattern.

                – Luke
                Jun 7 '13 at 19:42











              • @Luke: You can adapt the solution to your liking. I'm not entirely sure of your use-case. I've added an optional argument to newusedecl that allows you to update/specify the CSV delimiter. Default is ,.

                – Werner
                Jun 7 '13 at 20:02











              • There is an understandable exampke usage of this idea: davidyat.es/2016/07/27/…. I understood Werner's comments much better after reading that.

                – pauljohn32
                Mar 29 '18 at 9:15

















              Ah, that's nice. So the #1 in gobblenext is the next parameter contents, which I can process as necessary. I think that this is a nice alternative to the way that I'm doing it, but I'm not sure that it's more maintainable than what I've got. I'm going to accept it as an answer and just conclude that this isn't done often enough for there to be a "commonly accepted" pattern.

              – Luke
              Jun 7 '13 at 19:42





              Ah, that's nice. So the #1 in gobblenext is the next parameter contents, which I can process as necessary. I think that this is a nice alternative to the way that I'm doing it, but I'm not sure that it's more maintainable than what I've got. I'm going to accept it as an answer and just conclude that this isn't done often enough for there to be a "commonly accepted" pattern.

              – Luke
              Jun 7 '13 at 19:42













              @Luke: You can adapt the solution to your liking. I'm not entirely sure of your use-case. I've added an optional argument to newusedecl that allows you to update/specify the CSV delimiter. Default is ,.

              – Werner
              Jun 7 '13 at 20:02





              @Luke: You can adapt the solution to your liking. I'm not entirely sure of your use-case. I've added an optional argument to newusedecl that allows you to update/specify the CSV delimiter. Default is ,.

              – Werner
              Jun 7 '13 at 20:02













              There is an understandable exampke usage of this idea: davidyat.es/2016/07/27/…. I understood Werner's comments much better after reading that.

              – pauljohn32
              Mar 29 '18 at 9:15





              There is an understandable exampke usage of this idea: davidyat.es/2016/07/27/…. I understood Werner's comments much better after reading that.

              – pauljohn32
              Mar 29 '18 at 9:15











              3














              You've not given us much to go off of, but here's something that seems to implement what you want while being fed a comma separated list (in lieu of passing a variable number of arguments).



              documentclass{article}
              usepackage{xparse}
              newcounter{myargcounter}
              ExplSyntaxOn
              clist_new:N l_myvararg_parameters_clist
              tl_new:N l_myvararg_current_item_tl
              NewDocumentCommand{makecsv}{ m }
              {
              clist_set:Nn l_myvararg_parameters_clist { #1 }
              int_while_do:nNnn { clist_count:N l_myvararg_parameters_clist } > { 1 }
              {
              clist_pop:NN l_myvararg_parameters_clist l_myvararg_current_item_tl
              tl_use:N l_myvararg_current_item_tl,
              }
              clist_pop:NN l_myvararg_parameters_clist
              l_myvararg_current_item_tl
              {} ~ and ~ tl_use:N l_myvararg_current_item_tl
              }
              ExplSyntaxOff
              pagestyle{empty}
              begin{document}
              makecsv{a,b,c,d}

              makecsv{a,b,c,d,e,f,g}

              makecsv{a,b}

              end{document}





              share|improve this answer
























              • Thanks, but as previously stated, I don't have the flexibility to adjust the use-site of makecsv, nor do I want to rely on any non-2e stuff.

                – Luke
                Jun 7 '13 at 19:09











              • I'm not sure I understand what you mean by use-site.

                – A.Ellett
                Jun 7 '13 at 19:26











              • I mean as opposed to definition site. That is, I control the .sty where makecsv it is defined, but I am stuck with the way that .tex authors are already using it. I can't get them to change from makecsv{c}{p1}{p2}{p3} to makecsv{p1, p2, p3} without a meeting that I don't want to have, since I can actually provide what they want without the API change.

                – Luke
                Jun 7 '13 at 19:33











              • This is a valuable answer! Even though OP cannot use it, readers should not overlook. Similar solution tex.stackexchange.com/questions/417064/…. Only caution: require newer LaTeX distribution

                – pauljohn32
                Mar 29 '18 at 9:22


















              3














              You've not given us much to go off of, but here's something that seems to implement what you want while being fed a comma separated list (in lieu of passing a variable number of arguments).



              documentclass{article}
              usepackage{xparse}
              newcounter{myargcounter}
              ExplSyntaxOn
              clist_new:N l_myvararg_parameters_clist
              tl_new:N l_myvararg_current_item_tl
              NewDocumentCommand{makecsv}{ m }
              {
              clist_set:Nn l_myvararg_parameters_clist { #1 }
              int_while_do:nNnn { clist_count:N l_myvararg_parameters_clist } > { 1 }
              {
              clist_pop:NN l_myvararg_parameters_clist l_myvararg_current_item_tl
              tl_use:N l_myvararg_current_item_tl,
              }
              clist_pop:NN l_myvararg_parameters_clist
              l_myvararg_current_item_tl
              {} ~ and ~ tl_use:N l_myvararg_current_item_tl
              }
              ExplSyntaxOff
              pagestyle{empty}
              begin{document}
              makecsv{a,b,c,d}

              makecsv{a,b,c,d,e,f,g}

              makecsv{a,b}

              end{document}





              share|improve this answer
























              • Thanks, but as previously stated, I don't have the flexibility to adjust the use-site of makecsv, nor do I want to rely on any non-2e stuff.

                – Luke
                Jun 7 '13 at 19:09











              • I'm not sure I understand what you mean by use-site.

                – A.Ellett
                Jun 7 '13 at 19:26











              • I mean as opposed to definition site. That is, I control the .sty where makecsv it is defined, but I am stuck with the way that .tex authors are already using it. I can't get them to change from makecsv{c}{p1}{p2}{p3} to makecsv{p1, p2, p3} without a meeting that I don't want to have, since I can actually provide what they want without the API change.

                – Luke
                Jun 7 '13 at 19:33











              • This is a valuable answer! Even though OP cannot use it, readers should not overlook. Similar solution tex.stackexchange.com/questions/417064/…. Only caution: require newer LaTeX distribution

                – pauljohn32
                Mar 29 '18 at 9:22
















              3












              3








              3







              You've not given us much to go off of, but here's something that seems to implement what you want while being fed a comma separated list (in lieu of passing a variable number of arguments).



              documentclass{article}
              usepackage{xparse}
              newcounter{myargcounter}
              ExplSyntaxOn
              clist_new:N l_myvararg_parameters_clist
              tl_new:N l_myvararg_current_item_tl
              NewDocumentCommand{makecsv}{ m }
              {
              clist_set:Nn l_myvararg_parameters_clist { #1 }
              int_while_do:nNnn { clist_count:N l_myvararg_parameters_clist } > { 1 }
              {
              clist_pop:NN l_myvararg_parameters_clist l_myvararg_current_item_tl
              tl_use:N l_myvararg_current_item_tl,
              }
              clist_pop:NN l_myvararg_parameters_clist
              l_myvararg_current_item_tl
              {} ~ and ~ tl_use:N l_myvararg_current_item_tl
              }
              ExplSyntaxOff
              pagestyle{empty}
              begin{document}
              makecsv{a,b,c,d}

              makecsv{a,b,c,d,e,f,g}

              makecsv{a,b}

              end{document}





              share|improve this answer













              You've not given us much to go off of, but here's something that seems to implement what you want while being fed a comma separated list (in lieu of passing a variable number of arguments).



              documentclass{article}
              usepackage{xparse}
              newcounter{myargcounter}
              ExplSyntaxOn
              clist_new:N l_myvararg_parameters_clist
              tl_new:N l_myvararg_current_item_tl
              NewDocumentCommand{makecsv}{ m }
              {
              clist_set:Nn l_myvararg_parameters_clist { #1 }
              int_while_do:nNnn { clist_count:N l_myvararg_parameters_clist } > { 1 }
              {
              clist_pop:NN l_myvararg_parameters_clist l_myvararg_current_item_tl
              tl_use:N l_myvararg_current_item_tl,
              }
              clist_pop:NN l_myvararg_parameters_clist
              l_myvararg_current_item_tl
              {} ~ and ~ tl_use:N l_myvararg_current_item_tl
              }
              ExplSyntaxOff
              pagestyle{empty}
              begin{document}
              makecsv{a,b,c,d}

              makecsv{a,b,c,d,e,f,g}

              makecsv{a,b}

              end{document}






              share|improve this answer












              share|improve this answer



              share|improve this answer










              answered Jun 7 '13 at 18:59









              A.EllettA.Ellett

              36.6k1169177




              36.6k1169177













              • Thanks, but as previously stated, I don't have the flexibility to adjust the use-site of makecsv, nor do I want to rely on any non-2e stuff.

                – Luke
                Jun 7 '13 at 19:09











              • I'm not sure I understand what you mean by use-site.

                – A.Ellett
                Jun 7 '13 at 19:26











              • I mean as opposed to definition site. That is, I control the .sty where makecsv it is defined, but I am stuck with the way that .tex authors are already using it. I can't get them to change from makecsv{c}{p1}{p2}{p3} to makecsv{p1, p2, p3} without a meeting that I don't want to have, since I can actually provide what they want without the API change.

                – Luke
                Jun 7 '13 at 19:33











              • This is a valuable answer! Even though OP cannot use it, readers should not overlook. Similar solution tex.stackexchange.com/questions/417064/…. Only caution: require newer LaTeX distribution

                – pauljohn32
                Mar 29 '18 at 9:22





















              • Thanks, but as previously stated, I don't have the flexibility to adjust the use-site of makecsv, nor do I want to rely on any non-2e stuff.

                – Luke
                Jun 7 '13 at 19:09











              • I'm not sure I understand what you mean by use-site.

                – A.Ellett
                Jun 7 '13 at 19:26











              • I mean as opposed to definition site. That is, I control the .sty where makecsv it is defined, but I am stuck with the way that .tex authors are already using it. I can't get them to change from makecsv{c}{p1}{p2}{p3} to makecsv{p1, p2, p3} without a meeting that I don't want to have, since I can actually provide what they want without the API change.

                – Luke
                Jun 7 '13 at 19:33











              • This is a valuable answer! Even though OP cannot use it, readers should not overlook. Similar solution tex.stackexchange.com/questions/417064/…. Only caution: require newer LaTeX distribution

                – pauljohn32
                Mar 29 '18 at 9:22



















              Thanks, but as previously stated, I don't have the flexibility to adjust the use-site of makecsv, nor do I want to rely on any non-2e stuff.

              – Luke
              Jun 7 '13 at 19:09





              Thanks, but as previously stated, I don't have the flexibility to adjust the use-site of makecsv, nor do I want to rely on any non-2e stuff.

              – Luke
              Jun 7 '13 at 19:09













              I'm not sure I understand what you mean by use-site.

              – A.Ellett
              Jun 7 '13 at 19:26





              I'm not sure I understand what you mean by use-site.

              – A.Ellett
              Jun 7 '13 at 19:26













              I mean as opposed to definition site. That is, I control the .sty where makecsv it is defined, but I am stuck with the way that .tex authors are already using it. I can't get them to change from makecsv{c}{p1}{p2}{p3} to makecsv{p1, p2, p3} without a meeting that I don't want to have, since I can actually provide what they want without the API change.

              – Luke
              Jun 7 '13 at 19:33





              I mean as opposed to definition site. That is, I control the .sty where makecsv it is defined, but I am stuck with the way that .tex authors are already using it. I can't get them to change from makecsv{c}{p1}{p2}{p3} to makecsv{p1, p2, p3} without a meeting that I don't want to have, since I can actually provide what they want without the API change.

              – Luke
              Jun 7 '13 at 19:33













              This is a valuable answer! Even though OP cannot use it, readers should not overlook. Similar solution tex.stackexchange.com/questions/417064/…. Only caution: require newer LaTeX distribution

              – pauljohn32
              Mar 29 '18 at 9:22







              This is a valuable answer! Even though OP cannot use it, readers should not overlook. Similar solution tex.stackexchange.com/questions/417064/…. Only caution: require newer LaTeX distribution

              – pauljohn32
              Mar 29 '18 at 9:22













              0














              Here is an expansion-based implementation where the loop for gathering the parameters is based on romannumeral both for triggering expansion and for keeping track of the amount of parameters that still is to be collected.



              The usedecl-mechanism does without using any count-registers and without whatsoever temporary assignments for carrying out the loop. e-TeX-extensions and whatsoever fancy packages are not needed. ;-)



              Due to romannumeral-expansion usedecl delivers the result after two expansion-steps/after being hit by expandafter twice.



              documentclass{report}

              makeatletter
              %%----------------------------------------------------------------------
              %% Form control sequence token from sequence of characters denoting its
              %% name:
              %% UD@name foo{bar} -> foobar
              %% , e.g.,
              %% UD@namenewcommand*{foo} -> newcommand*foo
              %% UD@namestring{foo} -> stringfoo
              %% UD@nameUD@namelet{foo}={bar} -> UD@nameletfoo={bar} -> letfoo=bar
              %% UD@name{foo} -> foo
              %%......................................................................
              @ifdefinableUD@name{%
              longdefUD@name#1#{romannumeral0UD@innername{#1}}%
              }%
              newcommandUD@innername[2]{%
              expandafterUD@exchangeexpandafter{csname#2endcsname}{ #1}%
              }%
              newcommandUD@exchange[2]{#2#1}%
              %%----------------------------------------------------------------------
              %% Check whether argument is empty:
              %%......................................................................
              %% UD@CheckWhetherNull{<Argument which is to be checked>}%
              %% {<Tokens to be delivered in case that argument
              %% which is to be checked is empty>}%
              %% {<Tokens to be delivered in case that argument
              %% which is to be checked is not empty>}%
              %% The gist of this macro comes from Robert R. Schneck's ifempty-macro:
              %% <https://groups.google.com/forum/#!original/comp.text.tex/kuOEIQIrElc/lUg37FmhA74J>
              newcommandUD@CheckWhetherNull[1]{%
              romannumeral0expandafter@secondoftwostring{expandafter
              @secondoftwoexpandafter{expandafter{string#1}expandafter
              @secondoftwostring}expandafter@firstoftwoexpandafter{expandafter
              @secondoftwostring}expandafterexpandafter@firstoftwo{ }{}%
              @secondoftwo}{expandafterexpandafter@firstoftwo{ }{}@firstoftwo}%
              }%
              %%----------------------------------------------------------------------
              %% Associate IDs with amounts of parameters and symbols:
              %% No count-registers get allocated/get wasted. Just macros get defined.
              %%......................................................................
              newcommanddecl[3]{%
              % #1: decl id; #2: decl symbol; #3: # params
              UD@namenewcommand*{decl@#1}{#2}%
              UD@namenewcommand*{decl@#1@nparams}{#3}%
              }%
              %%----------------------------------------------------------------------
              %% usedecl{<decl id>}<list of parameters of length decl@<decl id>@nparams>
              %%
              %% creates comma list from parameters, nested in parentheses and
              %% lead by the tokens that come from expanding decl@<decl id>
              %%
              %% Due to romannumeral-expansion the result is delivered after two
              %% expansion-steps/after "hitting" usedecl by expandafter twice.
              %%......................................................................
              newcommandusedecl[1]{%
              romannumeral0%
              expandafterUD@exchange
              expandafter{%
              expandafter{%
              romannumeral0%
              UD@exchange{ }{UD@nameexpandafter}{decl@#1}%
              }{}{}%
              }{%
              expandafterusedeclloop
              expandafter{%
              romannumeralUD@namenumbernumber{decl@#1@nparams} 000 %
              }%
              }%
              }%
              newcommandusedeclloop[4]{%
              %#1 amount of m = amount of parameters to collect
              %#2 decl symbol
              %#3 prepend-tokens
              %#4 parameters collected so far
              UD@CheckWhetherNull{#1}{ #2(#4)}{%
              expandafterusedeclloopatfetchexpandafter{@gobble#1}{#2}{, }{#4#3}%
              }%
              }%
              newcommandusedeclloopatfetch[5]{%
              %#1 amount of m = amount of parameters to collect
              %#2 decl symbol
              %#3 prepend tokens
              %#4 parameters collected so far
              %#5 next parameter fetched.
              usedeclloop{#1}{#2}{#3}{#4#5}%
              }%
              newcommandwithoutdecl[2]{%
              romannumeral0%
              expandafterusedeclloopexpandafter{romannumeralnumbernumber#2 000 }{#1}%
              {}{}%
              }%

              makeatother

              % declare the symbols and the amounts of parameters:
              decl{foo}{FOO}{3}
              decl{bar}{BAR}{4}

              begin{document}

              usedecl{foo}{p1}{p2}{p3}par
              usedecl{bar}{p1}{p2}{p3}{p4}par
              withoutdecl{BAZ}{5}{p1}{p2}{p3}{p4}{p5}par

              expandafterexpandafterexpandafterdef
              expandafterexpandafterexpandafterfoolist
              expandafterexpandafterexpandafter{%
              usedecl{foo}{p1}{p2}{p3}%
              }%

              expandafterexpandafterexpandafterdef
              expandafterexpandafterexpandafterbarlist
              expandafterexpandafterexpandafter{%
              usedecl{bar}{p1}{p2}{p3}{p4}%
              }%

              expandafterexpandafterexpandafterdef
              expandafterexpandafterexpandafterbazlist
              expandafterexpandafterexpandafter{%
              withoutdecl{BAZ}{5}{p1}{p2}{p3}{p4}{p5}%
              }%

              texttt{|frenchspacingstringfoolist=meaningfoolist|}

              texttt{|frenchspacingstringbarlist=meaningbarlist|}

              texttt{|frenchspacingstringbazlist=meaningbazlist|}

              end{document}


              enter image description here





              share




























                0














                Here is an expansion-based implementation where the loop for gathering the parameters is based on romannumeral both for triggering expansion and for keeping track of the amount of parameters that still is to be collected.



                The usedecl-mechanism does without using any count-registers and without whatsoever temporary assignments for carrying out the loop. e-TeX-extensions and whatsoever fancy packages are not needed. ;-)



                Due to romannumeral-expansion usedecl delivers the result after two expansion-steps/after being hit by expandafter twice.



                documentclass{report}

                makeatletter
                %%----------------------------------------------------------------------
                %% Form control sequence token from sequence of characters denoting its
                %% name:
                %% UD@name foo{bar} -> foobar
                %% , e.g.,
                %% UD@namenewcommand*{foo} -> newcommand*foo
                %% UD@namestring{foo} -> stringfoo
                %% UD@nameUD@namelet{foo}={bar} -> UD@nameletfoo={bar} -> letfoo=bar
                %% UD@name{foo} -> foo
                %%......................................................................
                @ifdefinableUD@name{%
                longdefUD@name#1#{romannumeral0UD@innername{#1}}%
                }%
                newcommandUD@innername[2]{%
                expandafterUD@exchangeexpandafter{csname#2endcsname}{ #1}%
                }%
                newcommandUD@exchange[2]{#2#1}%
                %%----------------------------------------------------------------------
                %% Check whether argument is empty:
                %%......................................................................
                %% UD@CheckWhetherNull{<Argument which is to be checked>}%
                %% {<Tokens to be delivered in case that argument
                %% which is to be checked is empty>}%
                %% {<Tokens to be delivered in case that argument
                %% which is to be checked is not empty>}%
                %% The gist of this macro comes from Robert R. Schneck's ifempty-macro:
                %% <https://groups.google.com/forum/#!original/comp.text.tex/kuOEIQIrElc/lUg37FmhA74J>
                newcommandUD@CheckWhetherNull[1]{%
                romannumeral0expandafter@secondoftwostring{expandafter
                @secondoftwoexpandafter{expandafter{string#1}expandafter
                @secondoftwostring}expandafter@firstoftwoexpandafter{expandafter
                @secondoftwostring}expandafterexpandafter@firstoftwo{ }{}%
                @secondoftwo}{expandafterexpandafter@firstoftwo{ }{}@firstoftwo}%
                }%
                %%----------------------------------------------------------------------
                %% Associate IDs with amounts of parameters and symbols:
                %% No count-registers get allocated/get wasted. Just macros get defined.
                %%......................................................................
                newcommanddecl[3]{%
                % #1: decl id; #2: decl symbol; #3: # params
                UD@namenewcommand*{decl@#1}{#2}%
                UD@namenewcommand*{decl@#1@nparams}{#3}%
                }%
                %%----------------------------------------------------------------------
                %% usedecl{<decl id>}<list of parameters of length decl@<decl id>@nparams>
                %%
                %% creates comma list from parameters, nested in parentheses and
                %% lead by the tokens that come from expanding decl@<decl id>
                %%
                %% Due to romannumeral-expansion the result is delivered after two
                %% expansion-steps/after "hitting" usedecl by expandafter twice.
                %%......................................................................
                newcommandusedecl[1]{%
                romannumeral0%
                expandafterUD@exchange
                expandafter{%
                expandafter{%
                romannumeral0%
                UD@exchange{ }{UD@nameexpandafter}{decl@#1}%
                }{}{}%
                }{%
                expandafterusedeclloop
                expandafter{%
                romannumeralUD@namenumbernumber{decl@#1@nparams} 000 %
                }%
                }%
                }%
                newcommandusedeclloop[4]{%
                %#1 amount of m = amount of parameters to collect
                %#2 decl symbol
                %#3 prepend-tokens
                %#4 parameters collected so far
                UD@CheckWhetherNull{#1}{ #2(#4)}{%
                expandafterusedeclloopatfetchexpandafter{@gobble#1}{#2}{, }{#4#3}%
                }%
                }%
                newcommandusedeclloopatfetch[5]{%
                %#1 amount of m = amount of parameters to collect
                %#2 decl symbol
                %#3 prepend tokens
                %#4 parameters collected so far
                %#5 next parameter fetched.
                usedeclloop{#1}{#2}{#3}{#4#5}%
                }%
                newcommandwithoutdecl[2]{%
                romannumeral0%
                expandafterusedeclloopexpandafter{romannumeralnumbernumber#2 000 }{#1}%
                {}{}%
                }%

                makeatother

                % declare the symbols and the amounts of parameters:
                decl{foo}{FOO}{3}
                decl{bar}{BAR}{4}

                begin{document}

                usedecl{foo}{p1}{p2}{p3}par
                usedecl{bar}{p1}{p2}{p3}{p4}par
                withoutdecl{BAZ}{5}{p1}{p2}{p3}{p4}{p5}par

                expandafterexpandafterexpandafterdef
                expandafterexpandafterexpandafterfoolist
                expandafterexpandafterexpandafter{%
                usedecl{foo}{p1}{p2}{p3}%
                }%

                expandafterexpandafterexpandafterdef
                expandafterexpandafterexpandafterbarlist
                expandafterexpandafterexpandafter{%
                usedecl{bar}{p1}{p2}{p3}{p4}%
                }%

                expandafterexpandafterexpandafterdef
                expandafterexpandafterexpandafterbazlist
                expandafterexpandafterexpandafter{%
                withoutdecl{BAZ}{5}{p1}{p2}{p3}{p4}{p5}%
                }%

                texttt{|frenchspacingstringfoolist=meaningfoolist|}

                texttt{|frenchspacingstringbarlist=meaningbarlist|}

                texttt{|frenchspacingstringbazlist=meaningbazlist|}

                end{document}


                enter image description here





                share


























                  0












                  0








                  0







                  Here is an expansion-based implementation where the loop for gathering the parameters is based on romannumeral both for triggering expansion and for keeping track of the amount of parameters that still is to be collected.



                  The usedecl-mechanism does without using any count-registers and without whatsoever temporary assignments for carrying out the loop. e-TeX-extensions and whatsoever fancy packages are not needed. ;-)



                  Due to romannumeral-expansion usedecl delivers the result after two expansion-steps/after being hit by expandafter twice.



                  documentclass{report}

                  makeatletter
                  %%----------------------------------------------------------------------
                  %% Form control sequence token from sequence of characters denoting its
                  %% name:
                  %% UD@name foo{bar} -> foobar
                  %% , e.g.,
                  %% UD@namenewcommand*{foo} -> newcommand*foo
                  %% UD@namestring{foo} -> stringfoo
                  %% UD@nameUD@namelet{foo}={bar} -> UD@nameletfoo={bar} -> letfoo=bar
                  %% UD@name{foo} -> foo
                  %%......................................................................
                  @ifdefinableUD@name{%
                  longdefUD@name#1#{romannumeral0UD@innername{#1}}%
                  }%
                  newcommandUD@innername[2]{%
                  expandafterUD@exchangeexpandafter{csname#2endcsname}{ #1}%
                  }%
                  newcommandUD@exchange[2]{#2#1}%
                  %%----------------------------------------------------------------------
                  %% Check whether argument is empty:
                  %%......................................................................
                  %% UD@CheckWhetherNull{<Argument which is to be checked>}%
                  %% {<Tokens to be delivered in case that argument
                  %% which is to be checked is empty>}%
                  %% {<Tokens to be delivered in case that argument
                  %% which is to be checked is not empty>}%
                  %% The gist of this macro comes from Robert R. Schneck's ifempty-macro:
                  %% <https://groups.google.com/forum/#!original/comp.text.tex/kuOEIQIrElc/lUg37FmhA74J>
                  newcommandUD@CheckWhetherNull[1]{%
                  romannumeral0expandafter@secondoftwostring{expandafter
                  @secondoftwoexpandafter{expandafter{string#1}expandafter
                  @secondoftwostring}expandafter@firstoftwoexpandafter{expandafter
                  @secondoftwostring}expandafterexpandafter@firstoftwo{ }{}%
                  @secondoftwo}{expandafterexpandafter@firstoftwo{ }{}@firstoftwo}%
                  }%
                  %%----------------------------------------------------------------------
                  %% Associate IDs with amounts of parameters and symbols:
                  %% No count-registers get allocated/get wasted. Just macros get defined.
                  %%......................................................................
                  newcommanddecl[3]{%
                  % #1: decl id; #2: decl symbol; #3: # params
                  UD@namenewcommand*{decl@#1}{#2}%
                  UD@namenewcommand*{decl@#1@nparams}{#3}%
                  }%
                  %%----------------------------------------------------------------------
                  %% usedecl{<decl id>}<list of parameters of length decl@<decl id>@nparams>
                  %%
                  %% creates comma list from parameters, nested in parentheses and
                  %% lead by the tokens that come from expanding decl@<decl id>
                  %%
                  %% Due to romannumeral-expansion the result is delivered after two
                  %% expansion-steps/after "hitting" usedecl by expandafter twice.
                  %%......................................................................
                  newcommandusedecl[1]{%
                  romannumeral0%
                  expandafterUD@exchange
                  expandafter{%
                  expandafter{%
                  romannumeral0%
                  UD@exchange{ }{UD@nameexpandafter}{decl@#1}%
                  }{}{}%
                  }{%
                  expandafterusedeclloop
                  expandafter{%
                  romannumeralUD@namenumbernumber{decl@#1@nparams} 000 %
                  }%
                  }%
                  }%
                  newcommandusedeclloop[4]{%
                  %#1 amount of m = amount of parameters to collect
                  %#2 decl symbol
                  %#3 prepend-tokens
                  %#4 parameters collected so far
                  UD@CheckWhetherNull{#1}{ #2(#4)}{%
                  expandafterusedeclloopatfetchexpandafter{@gobble#1}{#2}{, }{#4#3}%
                  }%
                  }%
                  newcommandusedeclloopatfetch[5]{%
                  %#1 amount of m = amount of parameters to collect
                  %#2 decl symbol
                  %#3 prepend tokens
                  %#4 parameters collected so far
                  %#5 next parameter fetched.
                  usedeclloop{#1}{#2}{#3}{#4#5}%
                  }%
                  newcommandwithoutdecl[2]{%
                  romannumeral0%
                  expandafterusedeclloopexpandafter{romannumeralnumbernumber#2 000 }{#1}%
                  {}{}%
                  }%

                  makeatother

                  % declare the symbols and the amounts of parameters:
                  decl{foo}{FOO}{3}
                  decl{bar}{BAR}{4}

                  begin{document}

                  usedecl{foo}{p1}{p2}{p3}par
                  usedecl{bar}{p1}{p2}{p3}{p4}par
                  withoutdecl{BAZ}{5}{p1}{p2}{p3}{p4}{p5}par

                  expandafterexpandafterexpandafterdef
                  expandafterexpandafterexpandafterfoolist
                  expandafterexpandafterexpandafter{%
                  usedecl{foo}{p1}{p2}{p3}%
                  }%

                  expandafterexpandafterexpandafterdef
                  expandafterexpandafterexpandafterbarlist
                  expandafterexpandafterexpandafter{%
                  usedecl{bar}{p1}{p2}{p3}{p4}%
                  }%

                  expandafterexpandafterexpandafterdef
                  expandafterexpandafterexpandafterbazlist
                  expandafterexpandafterexpandafter{%
                  withoutdecl{BAZ}{5}{p1}{p2}{p3}{p4}{p5}%
                  }%

                  texttt{|frenchspacingstringfoolist=meaningfoolist|}

                  texttt{|frenchspacingstringbarlist=meaningbarlist|}

                  texttt{|frenchspacingstringbazlist=meaningbazlist|}

                  end{document}


                  enter image description here





                  share













                  Here is an expansion-based implementation where the loop for gathering the parameters is based on romannumeral both for triggering expansion and for keeping track of the amount of parameters that still is to be collected.



                  The usedecl-mechanism does without using any count-registers and without whatsoever temporary assignments for carrying out the loop. e-TeX-extensions and whatsoever fancy packages are not needed. ;-)



                  Due to romannumeral-expansion usedecl delivers the result after two expansion-steps/after being hit by expandafter twice.



                  documentclass{report}

                  makeatletter
                  %%----------------------------------------------------------------------
                  %% Form control sequence token from sequence of characters denoting its
                  %% name:
                  %% UD@name foo{bar} -> foobar
                  %% , e.g.,
                  %% UD@namenewcommand*{foo} -> newcommand*foo
                  %% UD@namestring{foo} -> stringfoo
                  %% UD@nameUD@namelet{foo}={bar} -> UD@nameletfoo={bar} -> letfoo=bar
                  %% UD@name{foo} -> foo
                  %%......................................................................
                  @ifdefinableUD@name{%
                  longdefUD@name#1#{romannumeral0UD@innername{#1}}%
                  }%
                  newcommandUD@innername[2]{%
                  expandafterUD@exchangeexpandafter{csname#2endcsname}{ #1}%
                  }%
                  newcommandUD@exchange[2]{#2#1}%
                  %%----------------------------------------------------------------------
                  %% Check whether argument is empty:
                  %%......................................................................
                  %% UD@CheckWhetherNull{<Argument which is to be checked>}%
                  %% {<Tokens to be delivered in case that argument
                  %% which is to be checked is empty>}%
                  %% {<Tokens to be delivered in case that argument
                  %% which is to be checked is not empty>}%
                  %% The gist of this macro comes from Robert R. Schneck's ifempty-macro:
                  %% <https://groups.google.com/forum/#!original/comp.text.tex/kuOEIQIrElc/lUg37FmhA74J>
                  newcommandUD@CheckWhetherNull[1]{%
                  romannumeral0expandafter@secondoftwostring{expandafter
                  @secondoftwoexpandafter{expandafter{string#1}expandafter
                  @secondoftwostring}expandafter@firstoftwoexpandafter{expandafter
                  @secondoftwostring}expandafterexpandafter@firstoftwo{ }{}%
                  @secondoftwo}{expandafterexpandafter@firstoftwo{ }{}@firstoftwo}%
                  }%
                  %%----------------------------------------------------------------------
                  %% Associate IDs with amounts of parameters and symbols:
                  %% No count-registers get allocated/get wasted. Just macros get defined.
                  %%......................................................................
                  newcommanddecl[3]{%
                  % #1: decl id; #2: decl symbol; #3: # params
                  UD@namenewcommand*{decl@#1}{#2}%
                  UD@namenewcommand*{decl@#1@nparams}{#3}%
                  }%
                  %%----------------------------------------------------------------------
                  %% usedecl{<decl id>}<list of parameters of length decl@<decl id>@nparams>
                  %%
                  %% creates comma list from parameters, nested in parentheses and
                  %% lead by the tokens that come from expanding decl@<decl id>
                  %%
                  %% Due to romannumeral-expansion the result is delivered after two
                  %% expansion-steps/after "hitting" usedecl by expandafter twice.
                  %%......................................................................
                  newcommandusedecl[1]{%
                  romannumeral0%
                  expandafterUD@exchange
                  expandafter{%
                  expandafter{%
                  romannumeral0%
                  UD@exchange{ }{UD@nameexpandafter}{decl@#1}%
                  }{}{}%
                  }{%
                  expandafterusedeclloop
                  expandafter{%
                  romannumeralUD@namenumbernumber{decl@#1@nparams} 000 %
                  }%
                  }%
                  }%
                  newcommandusedeclloop[4]{%
                  %#1 amount of m = amount of parameters to collect
                  %#2 decl symbol
                  %#3 prepend-tokens
                  %#4 parameters collected so far
                  UD@CheckWhetherNull{#1}{ #2(#4)}{%
                  expandafterusedeclloopatfetchexpandafter{@gobble#1}{#2}{, }{#4#3}%
                  }%
                  }%
                  newcommandusedeclloopatfetch[5]{%
                  %#1 amount of m = amount of parameters to collect
                  %#2 decl symbol
                  %#3 prepend tokens
                  %#4 parameters collected so far
                  %#5 next parameter fetched.
                  usedeclloop{#1}{#2}{#3}{#4#5}%
                  }%
                  newcommandwithoutdecl[2]{%
                  romannumeral0%
                  expandafterusedeclloopexpandafter{romannumeralnumbernumber#2 000 }{#1}%
                  {}{}%
                  }%

                  makeatother

                  % declare the symbols and the amounts of parameters:
                  decl{foo}{FOO}{3}
                  decl{bar}{BAR}{4}

                  begin{document}

                  usedecl{foo}{p1}{p2}{p3}par
                  usedecl{bar}{p1}{p2}{p3}{p4}par
                  withoutdecl{BAZ}{5}{p1}{p2}{p3}{p4}{p5}par

                  expandafterexpandafterexpandafterdef
                  expandafterexpandafterexpandafterfoolist
                  expandafterexpandafterexpandafter{%
                  usedecl{foo}{p1}{p2}{p3}%
                  }%

                  expandafterexpandafterexpandafterdef
                  expandafterexpandafterexpandafterbarlist
                  expandafterexpandafterexpandafter{%
                  usedecl{bar}{p1}{p2}{p3}{p4}%
                  }%

                  expandafterexpandafterexpandafterdef
                  expandafterexpandafterexpandafterbazlist
                  expandafterexpandafterexpandafter{%
                  withoutdecl{BAZ}{5}{p1}{p2}{p3}{p4}{p5}%
                  }%

                  texttt{|frenchspacingstringfoolist=meaningfoolist|}

                  texttt{|frenchspacingstringbarlist=meaningbarlist|}

                  texttt{|frenchspacingstringbazlist=meaningbazlist|}

                  end{document}


                  enter image description here






                  share











                  share


                  share










                  answered 9 mins ago









                  Ulrich DiezUlrich Diez

                  5,520620




                  5,520620






























                      draft saved

                      draft discarded




















































                      Thanks for contributing an answer to TeX - LaTeX Stack Exchange!


                      • Please be sure to answer the question. Provide details and share your research!

                      But avoid



                      • Asking for help, clarification, or responding to other answers.

                      • Making statements based on opinion; back them up with references or personal experience.


                      To learn more, see our tips on writing great answers.




                      draft saved


                      draft discarded














                      StackExchange.ready(
                      function () {
                      StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2ftex.stackexchange.com%2fquestions%2f118114%2fcommands-that-may-take-a-variable-number-of-arguments%23new-answer', 'question_page');
                      }
                      );

                      Post as a guest















                      Required, but never shown





















































                      Required, but never shown














                      Required, but never shown












                      Required, but never shown







                      Required, but never shown

































                      Required, but never shown














                      Required, but never shown












                      Required, but never shown







                      Required, but never shown







                      Popular posts from this blog

                      El tren de la libertad Índice Antecedentes "Porque yo decido" Desarrollo de la...

                      Castillo d'Acher Características Menú de navegación

                      Connecting two nodes from the same mother node horizontallyTikZ: What EXACTLY does the the |- notation for...