5

Is there a way to give a "name" to the arguments of a command and use them in its definition? What I'd like to do is to be able to get the value of an argument with a "label" like #myargname instead of #<number>. For example, I'd like to define a \foocommand like

\newcommand{\foocommand}[#normal,#bold,#italics,#smallcaps,#emphasis]%
           {#normal \textbf{#bold} \textit{#italics} \textsc{#smallcaps} \emph{#emphasis}}

instead of

\newcommand{\foocommand}[5]%
           {#1 \textbf{#2} \textit{#3} \textsc{#4} \emph{#5}}
3
  • 5
    But the same input should prevail? As in \foocommand{one}{two}{three}{four}{five}, with no indication of what their components mean? Or would you rather have \foocommand{bold=two, smallcaps=four, emphasis=five, italics=three, normal=one}?
    – Werner
    Jun 16, 2020 at 16:34
  • 2
    see tex.stackexchange.com/a/549462/2388 Jun 16, 2020 at 16:39
  • If you have an up-to-date TeXLive, you should have the namedef package installed, otherwise check it out here. Section 4.1 of the documentation (“Extended \newcommand”) features an interface like the one you want. Jun 22, 2020 at 11:39

3 Answers 3

2

You can. Here's a proof of concept.

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn
\NewDocumentCommand{\newparametercommand}{smO{}m}
 {
  \IfBooleanTF { #1 }
   {
    \ntakwetet_newparametercommand:Nnnn \cs_set_nopar:cV { #2 } { #3 } { #4 }
   }
   {
    \ntakwetet_newparametercommand:Nnnn \cs_set:cV { #2 } { #3 } { #4 }
   }
 }

\tl_new:N \l__ntakwetet_replacement_tl
\int_new:N \l__ntakwetet_index_int
\cs_generate_variant:Nn \cs_set:Nn { cV }
\cs_generate_variant:Nn \cs_set_nopar:Nn { cV }

\cs_new_protected:Nn \ntakwetet_newparametercommand:Nnnn
 {
  \tl_set:Nn \l__ntakwetet_replacement_tl { #4 }
  % double the #
  \regex_replace_all:nnN { \cP. } { \cP\#\cP\# } \l__ntakwetet_replacement_tl
  % index for the parameters
  \int_zero:N \l__ntakwetet_index_int
  % substitute any item in the list of parameters with #<n>
  \clist_map_inline:nn { #3 }
   {
    \int_incr:N \l__ntakwetet_index_int
    \exp_args:Nne \regex_replace_all:nnN
     { ##1 } % search
     { \exp_not:n { \cP\# } \int_eval:n { \l__ntakwetet_index_int } } % replace
     \l__ntakwetet_replacement_tl
   }
  % define a temporary macro with #1=\cs_set:cV or \cs_set_nopar:cV
  #1 {__ntakwetet_temp:\prg_replicate:nn { \l__ntakwetet_index_int } { n } } % name
   \l__ntakwetet_replacement_tl % replacement
  \cs_new_eq:Nc #2 {__ntakwetet_temp:\prg_replicate:nn { \l__ntakwetet_index_int } { n } }
 }

\ExplSyntaxOff

\newparametercommand\foo[#abc,#def]{#abc,#def}

\newparametercommand*\baz[#abc,#def]{#abc--#def}

\newparametercommand{\foocommand}[#normal,#bold,#italics,#smallcaps,#emphasis]{%
  #normal \textbf{#bold} \textit{#italics} \textsc{#smallcaps} \emph{#emphasis}%
}

\begin{document}

\foo{a}{b}

\baz{a}{b}

\foocommand{normal}{bold}{italics}{smallcaps}{emphasis}

\end{document}

Each item in the comma separated list is searched for in the replacement text and substituted with #<n> for the current index.

Limitation: a keyword cannot have another keyword as initial segment. So #a and #aa would not work. Also one cannot use more than nine arguments, of course.

enter image description here

1

Not automatic, but simple and easy.

\documentclass{article}

\newcommand{\foo}[2]% #1=\first, #2=\second
{\bgroup% make definitions local
  \def\first{#1}%
  \def\second{#2}%
  Arguments are \first{} and \second.
\egroup}

\begin{document}
\foo{A}{B}
\end{document}

Alternate, but prone to errors.

\documentclass{article}

\newcommand{\foo}[1]% #1 = TeX definitions
{\bgroup% make definitions local
  #1
  Arguments are \first{} and \second.
\egroup}

\begin{document}
\foo{\def\first{A}\def\second{B}}
\end{document}
0

EDITED to remove the deficiency of the prior approach.

If you don't mind changing your syntax in the following ways:

  1. Change your symbolic-argument identifier from # to something that is not cat 6, here done with !

  2. Define the new command with \symbolicnewcommand, since \newcommand is already defined with a different syntax.

Thus, the syntax is something like:

\symbolicnewcommand{\foocommand}%
  [!normal,!bold,!italics,!smallcaps,!emphasis,!boxed]%
  {!normal{} \textbf{!bold} \textit{!italics} 
   \textsc{!smallcaps} \emph{!emphasis} \fbox{!boxed}}

Unlike my prior approach, this approach doesn't merely mimic the \newcommand behavior, but recreates it, by converting the symbolic arguments into explicit #1, #2, etc., (i.e., NOT placed inside a \def).

Here is the MWE. Note that in the \meaning of the defined macro, the symbolic arguments, for example, !normal, is converted into an explicit #1.

\documentclass{article}
\usepackage[T1]{fontenc}
\usepackage{lmodern}
\usepackage{listofitems,tokcycle}
\newtoks\mytoks
\newtoks\tmptoks
\def\OPENSgroup{\OPENSgroup}% quark
\def\CLOSEgroup{\CLOSEgroup}% quark
\def\SPACEquark{\SPACEquark}% quark

\newcommand{\addtotoks}[2]{#1\expandafter{\the#1#2}}
\newcommand{\xaddtotoks}[2]{%
  \expandafter\addtotoks\expandafter#1\expandafter{#2}}

\makeatletter
\long\def\symbolicnewcommand#1[#2]#3{%
  \readlist*\arglist{#2}%
  \stripgroupingtrue%
  \tokcycle{\addcytoks{##1}}%
    {\addcytoks{\OPENSgroup}\processtoks{##1}\addcytoks{\CLOSEgroup}}%
    {\addcytoks{##1}}{\addcytoks{\SPACEquark}}{#3}%
  \mytoks\expandafter{\the\cytoks}%
  \foreachitem\z\in\arglist[]{%
    \expandafter\setsepchar\expandafter{\z}%
    \expandafter\def\expandafter\tmp\expandafter{\the\mytoks}%
    \readlist*\cmdlist{\tmp}%
    \mytoks{}%
    \foreachitem\zz\in\cmdlist[]{%
      \ifnum\zzcnt=1\relax\else%
        \tmptoks{&}%
        \xaddtotoks\tmptoks{\zcnt}%
        \xaddtotoks\mytoks{\the\tmptoks}%
      \fi%
      \xaddtotoks\mytoks{\zz}%
    }%
  }%
%
  \setsepchar{&}%
  \expandafter\readlist\expandafter\cmdlist\expandafter{\the\mytoks}%
  \mytoks{}%
  \foreachitem\z\in\cmdlist[]{%
    \ifnum\zcnt=1\relax\else\addtotoks\mytoks{####}\fi%
    \xaddtotoks\mytoks{\z}%
  }%
%
  \expandafter\addBracesSpaces\expandafter{\the\mytoks}%
%
  \def\tmp{\newcommand{#1}[\arglistlen]}%
  \expandafter\tmp\expandafter{\the\mytoks}%
}

\newcommand\addBracesSpaces[1]{\mytoks{}\fooaux#1\endfoo}
\def\fooaux#1{%
  \tctestifx{\endfoo#1}{}
  {\tctestifx{\OPENSgroup#1}{\Z}
  {\tctestifx{\SPACEquark#1}{\addtotoks\mytoks{ }}
  {\addtotoks\mytoks{#1}}\fooaux}}%
}
\makeatother

\def\Z#1\CLOSEgroup{\addtotoks\mytoks{{#1}}\fooaux}

\symbolicnewcommand{\foocommand}%
  [!normal,!bold,!italics,!smallcaps,!emphasis,!boxed]%
  {!normal{} \textbf{!bold} \textit{!italics} 
   \textsc{!smallcaps} \emph{!emphasis} \fbox{!boxed}}
\begin{document}
\meaning\foocommand

\foocommand{To be,}{or not}{to be?}{That}{is the question.}{123}
\end{document}

enter image description here

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