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`
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
|
show 5 more comments
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
You are asking for a list. I think you can search TeX.SX to find a lot of examples. Related packages areetoolbox
orl3clist
.
– 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 genericmakelistfromargs{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 findmakecsv{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
|
show 5 more comments
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
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
macros recursion
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 areetoolbox
orl3clist
.
– 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 genericmakelistfromargs{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 findmakecsv{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
|
show 5 more comments
You are asking for a list. I think you can search TeX.SX to find a lot of examples. Related packages areetoolbox
orl3clist
.
– 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 genericmakelistfromargs{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 findmakecsv{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
|
show 5 more comments
4 Answers
4
active
oldest
votes
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
add a comment |
If you're willing to peek ahead, you can check whether there's "another argument" and keep gobbling them on the fly:
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
.
Ah, that's nice. So the#1
ingobblenext
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 tonewusedecl
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
add a comment |
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}
Thanks, but as previously stated, I don't have the flexibility to adjust the use-site ofmakecsv
, 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 wheremakecsv
it is defined, but I am stuck with the way that .tex authors are already using it. I can't get them to change frommakecsv{c}{p1}{p2}{p3}
tomakecsv{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
add a comment |
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}
add a comment |
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
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
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
add a comment |
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
add a comment |
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
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
edited Jun 8 '13 at 1:13
answered Jun 7 '13 at 20:02
QrrbrbirlbelQrrbrbirlbel
77.9k4184319
77.9k4184319
add a comment |
add a comment |
If you're willing to peek ahead, you can check whether there's "another argument" and keep gobbling them on the fly:
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
.
Ah, that's nice. So the#1
ingobblenext
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 tonewusedecl
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
add a comment |
If you're willing to peek ahead, you can check whether there's "another argument" and keep gobbling them on the fly:
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
.
Ah, that's nice. So the#1
ingobblenext
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 tonewusedecl
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
add a comment |
If you're willing to peek ahead, you can check whether there's "another argument" and keep gobbling them on the fly:
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
.
If you're willing to peek ahead, you can check whether there's "another argument" and keep gobbling them on the fly:
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
.
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
ingobblenext
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 tonewusedecl
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
add a comment |
Ah, that's nice. So the#1
ingobblenext
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 tonewusedecl
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
add a comment |
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}
Thanks, but as previously stated, I don't have the flexibility to adjust the use-site ofmakecsv
, 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 wheremakecsv
it is defined, but I am stuck with the way that .tex authors are already using it. I can't get them to change frommakecsv{c}{p1}{p2}{p3}
tomakecsv{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
add a comment |
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}
Thanks, but as previously stated, I don't have the flexibility to adjust the use-site ofmakecsv
, 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 wheremakecsv
it is defined, but I am stuck with the way that .tex authors are already using it. I can't get them to change frommakecsv{c}{p1}{p2}{p3}
tomakecsv{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
add a comment |
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}
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}
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 ofmakecsv
, 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 wheremakecsv
it is defined, but I am stuck with the way that .tex authors are already using it. I can't get them to change frommakecsv{c}{p1}{p2}{p3}
tomakecsv{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
add a comment |
Thanks, but as previously stated, I don't have the flexibility to adjust the use-site ofmakecsv
, 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 wheremakecsv
it is defined, but I am stuck with the way that .tex authors are already using it. I can't get them to change frommakecsv{c}{p1}{p2}{p3}
tomakecsv{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
add a comment |
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}
add a comment |
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}
add a comment |
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}
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}
answered 9 mins ago
Ulrich DiezUlrich Diez
5,520620
5,520620
add a comment |
add a comment |
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.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
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
You are asking for a list. I think you can search TeX.SX to find a lot of examples. Related packages are
etoolbox
orl3clist
.– 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 genericmakelistfromargs{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